I'm trying to translate this SO answer code into Python (and a bit from the other answer there) with the help of official documentation for PACKAGE_ID and PACKAGE_INFO.
I'm getting full package names by GetPackageFullName
, but some of my structures aren't formed well as OpenPackageInfoByFullName
gives Unicode characters that are jibberish to me. Also PackageIdFromFullName
returns ERROR_INSUFFICIENT_BUFFER when called.
EDIT: As Paul Cornelius noticed, my code had issues with PACKAGE_INFO_REFERENCE. Now I have a problem converting buffer I got to structure or I messed up something before that. I provided a code to be run in Windows 10, Edge or Store app should be open prior to run (or open some other app and change the hardcoded text in code). The output shows that packageName (as the example field) is None:
import ctypes
import ctypes.wintypes
from win32api import OpenProcess, CloseHandle
from win32con import PROCESS_QUERY_INFORMATION, PROCESS_VM_READ
from win32gui import EnumChildWindows, EnumWindows
from win32process import GetWindowThreadProcessId
ERROR_SUCCESS = 0x0
ERROR_INSUFFICIENT_BUFFER = 0x7A
PACKAGE_FILTER_ALL_LOADED = 0x00000000
PACKAGE_FILTER_HEAD = 0x00000010
PACKAGE_INFORMATION_FULL = 0x00000100
PROCESS_QUERY_LIMITED_INFORMATION = 0x1000
class PACKAGE_INFO_REFERENCE(ctypes.Structure):
_fields_ = [("reserved", ctypes.c_void_p)]
class PACKAGE_SUBVERSION(ctypes.Structure):
_fields_ = [
("Revision", ctypes.wintypes.ATOM),
("Build", ctypes.wintypes.ATOM),
("Minor", ctypes.wintypes.ATOM),
("Major", ctypes.wintypes.ATOM),
]
class PACKAGE_VERSION(ctypes.Union):
_fields_ = [("Version", ctypes.c_uint64), ("DUMMYSTRUCTNAME", PACKAGE_SUBVERSION)]
class PACKAGE_ID(ctypes.Union):
_fields_ = [
("reserved", ctypes.c_uint32),
("processorArchitecture", ctypes.c_uint32),
("version", PACKAGE_VERSION),
# ("VersionRevision", ctypes.wintypes.ATOM),
# ("VersionBuild", ctypes.wintypes.ATOM),
# ("VersionMinor", ctypes.wintypes.ATOM),
# ("VersionMajor", ctypes.wintypes.ATOM),
("name", ctypes.c_wchar_p),
("publisher", ctypes.c_wchar_p),
("resourceId", ctypes.c_wchar_p),
("publisherId", ctypes.c_wchar_p),
]
class PACKAGE_INFO(ctypes.Union):
_fields_ = [
("reserved", ctypes.c_uint32),
("flags", ctypes.c_uint32),
("path", ctypes.c_wchar_p),
("packageFullName", ctypes.c_wchar_p),
("packageFamilyName", ctypes.c_wchar_p),
("packageId", PACKAGE_ID),
]
def append_to_collection(element, collection):
collection.append(element)
return True
def get_children(hwnd):
children = []
try:
EnumChildWindows(hwnd, append_to_collection, children)
except:
pass
return children
def package_full_name_from_handle(handle):
length = ctypes.c_uint()
retval = ctypes.windll.kernel32.GetPackageFullName(
handle, ctypes.byref(length), None
)
assert retval == ERROR_INSUFFICIENT_BUFFER
full_name = ctypes.create_unicode_buffer(length.value + 1)
retval = ctypes.windll.kernel32.GetPackageFullName(
handle, ctypes.byref(length), full_name
)
assert retval == ERROR_SUCCESS
return full_name
def package_path_from_full_name(full_name):
length = ctypes.c_uint()
retval = ctypes.windll.kernel32.GetPackagePathByFullName(
ctypes.byref(full_name), ctypes.byref(length), None
)
assert retval == ERROR_INSUFFICIENT_BUFFER
package_path = ctypes.create_unicode_buffer(length.value)
retval = ctypes.windll.kernel32.GetPackagePathByFullName(
ctypes.byref(full_name), ctypes.byref(length), ctypes.byref(package_path)
)
assert retval == ERROR_SUCCESS
return package_path
def package_family_name_from_full_name(full_name):
length = ctypes.c_uint()
retval = ctypes.windll.kernel32.PackageFamilyNameFromFullName(
ctypes.byref(full_name), ctypes.byref(length), None
)
assert retval == ERROR_INSUFFICIENT_BUFFER
family_name = ctypes.create_unicode_buffer(length.value)
retval = ctypes.windll.kernel32.PackageFamilyNameFromFullName(
ctypes.byref(full_name), ctypes.byref(length), ctypes.byref(family_name)
)
assert retval == ERROR_SUCCESS
return family_name
def package_info_reference_from_full_name(full_name):
package_info_reference = ctypes.pointer(PACKAGE_INFO_REFERENCE())
retval = ctypes.windll.kernel32.OpenPackageInfoByFullName(
ctypes.byref(full_name), 0, ctypes.byref(package_info_reference)
)
assert retval == ERROR_SUCCESS
return package_info_reference
def package_info_buffer_from_reference(package_info_reference):
length = ctypes.c_uint(0)
count = ctypes.c_uint()
retval = ctypes.windll.kernel32.GetPackageInfo(
package_info_reference,
PACKAGE_FILTER_HEAD,
ctypes.byref(length),
None,
ctypes.byref(count),
)
assert retval == ERROR_INSUFFICIENT_BUFFER
buffer = ctypes.create_string_buffer(length.value)
retval = ctypes.windll.kernel32.GetPackageInfo(
package_info_reference,
PACKAGE_FILTER_HEAD,
ctypes.byref(length),
ctypes.byref(buffer),
ctypes.byref(count),
)
assert retval == ERROR_SUCCESS
return buffer
def get_package(hwnd):
hprocess = None
_, pid = GetWindowThreadProcessId(hwnd)
children = get_children(hwnd)
for child in children:
_, child_pid = GetWindowThreadProcessId(child)
if child_pid != pid:
# hprocess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, False, child_pid)
hprocess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, False, child_pid)
break
if hprocess is None:
return
full_name = package_full_name_from_handle(hprocess.handle)
if not (
"Microsoft.MicrosoftEdge" in full_name.value
or "Microsoft.WindowsStore" in full_name.value
):
return None
print("\n full name:", full_name.value)
package_path = package_path_from_full_name(full_name)
print(" package path:", package_path.value)
family_name = package_family_name_from_full_name(full_name)
print(" family name:", family_name.value)
package_info_reference = package_info_reference_from_full_name(full_name)
print(" info reference:", package_info_reference.contents.reserved)
package_info_buffer = package_info_buffer_from_reference(package_info_reference)
package_info = PACKAGE_INFO()
ctypes.memmove(
ctypes.addressof(package_info), package_info_buffer, ctypes.sizeof(package_info)
)
print("packageFullName:", package_info.packageFullName)
CloseHandle(hprocess)
ctypes.windll.kernel32.ClosePackageInfo(package_info_reference)
def get_windows():
hwnds = []
EnumWindows(append_to_collection, hwnds)
return hwnds
if __name__ == "__main__":
for hwnd in get_windows():
get_package(hwnd)
And the output is:
(venv) C:\dev\examples>python uwp.py
full name: Microsoft.MicrosoftEdge_44.17763.1.0_neutral__8wekyb3d8bbwe
package path: C:\Windows\SystemApps\Microsoft.MicrosoftEdge_8wekyb3d8bbwe
family name: Microsoft.MicrosoftEdge_8wekyb3d8bbwe
info reference: 4128769
packageFullName: None
full name: Microsoft.WindowsStore_11805.1001.49.0_x64__8wekyb3d8bbwe
package path: C:\Program Files\WindowsApps\Microsoft.WindowsStore_11805.1001.49.0_x64__8wekyb3d8bbwe
family name: Microsoft.WindowsStore_8wekyb3d8bbwe
info reference: 6619137
packageFullName: None
I would be grateful if someone takes a look at my code and reveals my mistakes.
I "translated" your code to full ctypes (without the win32 package).
I patched alongside debugging it, but basically:
- Some structures were declared as Union.
- Lot of mismatch between pointer to types and types.
- The
ctypes.memove
seemed ok, but I prefer <ctypes_struct>.from_buffer
instead (shorter).
Tested on Windows 10 1903 (x64) and python 3.7.0 (x64).
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import ctypes
import ctypes.wintypes
ERROR_SUCCESS = 0x0
ERROR_INSUFFICIENT_BUFFER = 0x7A
APPMODEL_ERROR_NO_PACKAGE = 15700
PACKAGE_FILTER_ALL_LOADED = 0x00000000
PACKAGE_FILTER_HEAD = 0x00000010
PACKAGE_INFORMATION_FULL = 0x00000100
PROCESS_QUERY_LIMITED_INFORMATION = 0x1000
PROCESS_QUERY_INFORMATION = 0x0400
PROCESS_VM_READ = 0x0010
class PACKAGE_INFO_REFERENCE(ctypes.Structure):
_fields_ = [
("reserved", ctypes.c_void_p)
]
class PACKAGE_SUBVERSION(ctypes.Structure):
_fields_ = [
("Revision", ctypes.wintypes.USHORT),
("Build", ctypes.wintypes.USHORT),
("Minor", ctypes.wintypes.USHORT),
("Major", ctypes.wintypes.USHORT),
]
class PACKAGE_VERSION_U(ctypes.Union):
_fields_ = [
("Version", ctypes.c_uint64),
("DUMMYSTRUCTNAME", PACKAGE_SUBVERSION),
]
class PACKAGE_VERSION(ctypes.Structure):
_anonymous_ = ("u", )
_fields_ = [
("u", PACKAGE_VERSION_U),
]
class PACKAGE_ID(ctypes.Structure):
_fields_ = [
("reserved", ctypes.c_uint32),
("processorArchitecture", ctypes.c_uint32),
("version", PACKAGE_VERSION),
("name", ctypes.c_wchar_p),
("publisher", ctypes.c_wchar_p),
("resourceId", ctypes.c_wchar_p),
("publisherId", ctypes.c_wchar_p),
]
class PACKAGE_INFO(ctypes.Structure):
_fields_ = [
("reserved", ctypes.c_uint32),
("flags", ctypes.c_uint32),
("path", ctypes.c_wchar_p),
("packageFullName", ctypes.c_wchar_p),
("packageFamilyName", ctypes.c_wchar_p),
("packageId", PACKAGE_ID),
]
_user32 = ctypes.WinDLL("user32", use_last_error=True)
_get_windows_thread_process_id = _user32.GetWindowThreadProcessId
_get_windows_thread_process_id.argtypes = (ctypes.wintypes.HWND, ctypes.POINTER(ctypes.wintypes.DWORD))
_get_windows_thread_process_id.restype = ctypes.wintypes.DWORD
WNDENUMPROC = ctypes.WINFUNCTYPE(ctypes.wintypes.BOOL, ctypes.wintypes.HWND, ctypes.wintypes.LPARAM)
_enum_child_windows = _user32.EnumChildWindows
_enum_child_windows.argtypes = (ctypes.wintypes.HWND, WNDENUMPROC, ctypes.wintypes.LPARAM)
_enum_child_windows.restype = ctypes.wintypes.BOOL
_enum_windows = _user32.EnumWindows
_enum_windows.argtypes = (WNDENUMPROC, ctypes.wintypes.LPARAM)
_enum_windows.restype = ctypes.wintypes.BOOL
_kernel32 = ctypes.WinDLL("kernel32", use_last_error=True)
_open_process = _kernel32.OpenProcess
_open_process.argtypes = (ctypes.wintypes.DWORD, ctypes.wintypes.BOOL, ctypes.wintypes.DWORD)
_open_process.restype = ctypes.wintypes.HANDLE
_close_handle = _kernel32.CloseHandle
_close_handle.argtypes = (ctypes.wintypes.HANDLE, )
_close_handle.restype = ctypes.wintypes.BOOL
_get_package_info = _kernel32.GetPackageInfo
_get_package_info.argtypes = (
PACKAGE_INFO_REFERENCE,
ctypes.c_uint32,
ctypes.POINTER(ctypes.c_uint32),
ctypes.POINTER(ctypes.c_uint8),
ctypes.POINTER(ctypes.c_uint32)
)
_get_package_info.restype = ctypes.wintypes.LONG
_get_package_full_name = _kernel32.GetPackageFullName
_get_package_full_name.argtypes = (ctypes.wintypes.HANDLE, ctypes.POINTER(ctypes.c_uint32), ctypes.wintypes.LPCWSTR)
_get_package_full_name.restype = ctypes.wintypes.LONG
_get_package_path_by_full_name = _kernel32.GetPackagePathByFullName
_get_package_path_by_full_name.argtypes = (ctypes.wintypes.LPCWSTR, ctypes.POINTER(ctypes.c_uint32), ctypes.wintypes.LPCWSTR)
_get_package_path_by_full_name.restype = ctypes.wintypes.LONG
_package_family_name_from_full_name = _kernel32.PackageFamilyNameFromFullName
_package_family_name_from_full_name.argtypes = (
ctypes.wintypes.LPCWSTR,
ctypes.POINTER(ctypes.c_uint32),
ctypes.wintypes.LPCWSTR)
_package_family_name_from_full_name.restype = ctypes.wintypes.LONG
_open_package_info_by_full_name = _kernel32.OpenPackageInfoByFullName
_open_package_info_by_full_name.argtypes = (
ctypes.wintypes.LPCWSTR,
ctypes.c_uint32,
ctypes.POINTER(PACKAGE_INFO_REFERENCE)
)
_open_package_info_by_full_name.restype = ctypes.wintypes.LONG
_close_package_info = _kernel32.ClosePackageInfo
_close_package_info.argtypes = (
PACKAGE_INFO_REFERENCE,
)
_close_package_info.restype = ctypes.wintypes.LONG
def get_children(hwnd):
children = []
def append_to_collection(element, param):
children.append(element)
return True
func = WNDENUMPROC(append_to_collection)
_enum_child_windows(hwnd, func, 0)
return children
def package_full_name_from_handle(handle):
length = ctypes.c_uint()
ret_val = _get_package_full_name(handle, ctypes.byref(length), None)
if ret_val == APPMODEL_ERROR_NO_PACKAGE:
print(f"package_full_name_from_handle: handle {handle:#x} has no package.")
return None
full_name = ctypes.create_unicode_buffer(length.value + 1)
ret_val = _get_package_full_name(handle, ctypes.byref(length), full_name)
if ret_val != ERROR_SUCCESS:
err = ctypes.WinError(ctypes.get_last_error())
print(f"package_full_name_from_handle: error -> {str(err)}")
return None
return full_name
def package_path_from_full_name(full_name):
length = ctypes.c_uint()
retval = _get_package_path_by_full_name(full_name, ctypes.byref(length), None)
if retval != ERROR_INSUFFICIENT_BUFFER:
raise ctypes.WinError(ctypes.get_last_error())
package_path = ctypes.create_unicode_buffer(length.value)
retval = _get_package_path_by_full_name(full_name, ctypes.byref(length), package_path)
if retval != ERROR_SUCCESS:
raise ctypes.WinError(ctypes.get_last_error())
return package_path
def package_family_name_from_full_name(full_name):
length = ctypes.c_uint()
retval = _package_family_name_from_full_name(full_name, ctypes.byref(length), None)
if retval != ERROR_INSUFFICIENT_BUFFER:
raise ctypes.WinError(ctypes.get_last_error())
family_name = ctypes.create_unicode_buffer(length.value)
retval = _package_family_name_from_full_name(full_name, ctypes.byref(length), family_name)
if retval != ERROR_SUCCESS:
raise ctypes.WinError(ctypes.get_last_error())
return family_name
def package_info_reference_from_full_name(full_name):
package_info_reference = ctypes.pointer(PACKAGE_INFO_REFERENCE())
retval = _open_package_info_by_full_name(full_name, 0, package_info_reference)
if retval != ERROR_SUCCESS:
raise ctypes.WinError(ctypes.get_last_error())
return package_info_reference
def package_info_buffer_from_reference(package_info_reference):
length = ctypes.c_uint(0)
count = ctypes.c_uint()
retval = _get_package_info(
package_info_reference.contents, # package_info_reference is already a pointer. We want its content.
PACKAGE_FILTER_HEAD,
ctypes.byref(length),
None,
ctypes.byref(count),
)
if retval != ERROR_INSUFFICIENT_BUFFER:
raise ctypes.WinError(ctypes.get_last_error())
buffer = ctypes.create_string_buffer(length.value)
buffer_bytes = ctypes.cast(buffer, ctypes.POINTER(ctypes.c_uint8))
retval = _get_package_info(
package_info_reference.contents,
PACKAGE_FILTER_HEAD,
ctypes.byref(length),
buffer_bytes,
ctypes.byref(count),
)
if retval != ERROR_SUCCESS:
raise ctypes.WinError(ctypes.get_last_error())
return buffer, length
def get_package(hwnd):
pid = ctypes.wintypes.DWORD()
_get_windows_thread_process_id(
hwnd,
ctypes.byref(pid)
)
hprocess = _open_process(PROCESS_QUERY_LIMITED_INFORMATION, False, pid)
full_name = package_full_name_from_handle(hprocess)
if not full_name:
return
else:
print(full_name.value)
'''
children = get_children(hwnd)
for child in children:
child_pid = ctypes.wintypes.DWORD(0)
_get_windows_thread_process_id(child, ctypes.byref(child_pid))
if child_pid != pid:
hprocess = _open_process(PROCESS_QUERY_LIMITED_INFORMATION, False, child_pid)
break
if hprocess is None:
return
full_name = package_full_name_from_handle(hprocess)
if full_name is None:
return
if not ("Microsoft.MicrosoftEdge" in full_name.value or "Microsoft.WindowsStore" in full_name.value):
return None
'''
print("=" * 79)
print("full name: ", full_name.value)
package_path = package_path_from_full_name(full_name)
print("package path: ", package_path.value)
family_name = package_family_name_from_full_name(full_name)
print("family name:", family_name.value)
package_info_reference = package_info_reference_from_full_name(full_name)
print("info reference:", package_info_reference.contents.reserved)
package_info_buffer, length = package_info_buffer_from_reference(package_info_reference)
# size_package_info = ctypes.sizeof(PACKAGE_INFO)
# print(f"PACKAGE_INFO size: {size_package_info:#x}")
# print(f"num package info: {length.value / size_package_info}")
package_info = PACKAGE_INFO.from_buffer(package_info_buffer)
print("packageFullName:", package_info.packageFullName)
print("=" * 79)
_close_handle(hprocess)
_close_package_info(package_info_reference.contents)
def get_windows():
hwnds = []
def append_to_collection(element, param):
hwnds.append(element)
return True
func = WNDENUMPROC(append_to_collection)
_enum_windows(func, 0)
return hwnds
if __name__ == "__main__":
for hwnd in get_windows():
get_package(hwnd)
Sample output:
===============================================================================
full name: Microsoft.Windows.Cortana_1.12.3.18362_neutral_neutral_cw5n1h2txyewy
package path: C:\Windows\SystemApps\Microsoft.Windows.Cortana_cw5n1h2txyewy
family name: Microsoft.Windows.Cortana_cw5n1h2txyewy
info reference: 2882563979504
packageFullName: Microsoft.Windows.Cortana_1.12.3.18362_neutral_neutral_cw5n1h2txyewy
===============================================================================