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:
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).
Sample output: