Reputation: 11
Python developers need to set certain window properties for their applications to work correctly with the Windows taskbar (correct icon and grouping, and the ability to pin). In my case a PyQt5 application.
Its possible to use pywin32 to access the property store like so:
from win32com.propsys import propsys, pscon
import pythoncom
def setWindowProperties(hwnd, app_id, display_name, relaunch_path):
propStore = propsys.SHGetPropertyStoreForWindow(hwnd, propsys.IID_IPropertyStore)
propStore.SetValue(pscon.PKEY_AppUserModel_ID, propsys.PROPVARIANTType(app_id, pythoncom.VT_ILLEGAL))
propStore.SetValue(pscon.PKEY_AppUserModel_RelaunchDisplayNameResource, propsys.PROPVARIANTType(display_name, pythoncom.VT_ILLEGAL))
propStore.SetValue(pscon.PKEY_AppUserModel_RelaunchCommand, propsys.PROPVARIANTType(relaunch_path, pythoncom.VT_ILLEGAL))
propStore.Commit()
hwnd = ... # Window handle (for Qt see QWindow::winId() -> int(engine.rootObjects()[0].winId()))
app_id = "CompanyName.ProductName.Version"
display_name = "ProductName"
relaunch_path = "C:\\path\\to\\exe"
setWindowProperties(hwnd, app_id, display_name, relaunch_path)
This theoretically can be done with only ctypes to avoid a dependency on pywin32, but information about implementing this is scarce.
Upvotes: 1
Views: 206
Reputation: 11
import ctypes
from ctypes import wintypes
GUID = ctypes.c_ubyte * 16
class PROPERTYKEY(ctypes.Structure):
_fields_ = [("fmtid", GUID),
("pid", wintypes.DWORD)]
class PROPVARIANT(ctypes.Structure):
_fields_ = [("vt", wintypes.USHORT),
("wReserved1", wintypes.USHORT),
("wReserved2", wintypes.USHORT),
("wReserved3", wintypes.USHORT),
("pszVal", wintypes.LPWSTR)]
class IPropertyStoreVtbl(ctypes.Structure):
_fields_ = [
('QueryInterface', ctypes.CFUNCTYPE(ctypes.HRESULT, ctypes.c_void_p, ctypes.POINTER(GUID), ctypes.POINTER(ctypes.c_void_p))),
('AddRef', ctypes.CFUNCTYPE(ctypes.c_ulong, ctypes.c_void_p)),
('Release', ctypes.CFUNCTYPE(ctypes.c_ulong, ctypes.c_void_p)),
('GetCount', ctypes.CFUNCTYPE(ctypes.HRESULT, ctypes.c_void_p, ctypes.POINTER(ctypes.c_ulong))),
('GetAt', ctypes.CFUNCTYPE(ctypes.HRESULT, ctypes.c_void_p, ctypes.c_ulong, ctypes.POINTER(PROPERTYKEY))),
('GetValue', ctypes.CFUNCTYPE(ctypes.HRESULT, ctypes.c_void_p, ctypes.POINTER(PROPERTYKEY), ctypes.POINTER(PROPVARIANT))),
('SetValue', ctypes.CFUNCTYPE(ctypes.HRESULT, ctypes.c_void_p, ctypes.POINTER(PROPERTYKEY), ctypes.POINTER(PROPVARIANT))),
('Commit', ctypes.CFUNCTYPE(ctypes.HRESULT, ctypes.c_void_p))
]
class IPropertyStore(ctypes.Structure):
_fields_ = [('lpVtbl', ctypes.POINTER(IPropertyStoreVtbl))]
# {886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99}
IID_IPropertyStore = (GUID)(*bytearray.fromhex("eb8e6d88f28c46448d02cdba1dbdcf99"))
# {9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}
PKEY_AppUserModel = (GUID)(*bytearray.fromhex("55284c9f799f394ba8d0e1d42de1d5f3"))
ctypes.windll.ole32.CoInitialize.restype = ctypes.HRESULT
ctypes.windll.ole32.CoInitialize.argtypes = [ctypes.c_void_p]
ctypes.windll.ole32.CoUninitialize.restype = None
ctypes.windll.ole32.CoUninitialize.argtypes = None
ctypes.windll.shell32.SHGetPropertyStoreForWindow.restype = ctypes.HRESULT
ctypes.windll.shell32.SHGetPropertyStoreForWindow.argtypes = [wintypes.HWND, ctypes.POINTER(GUID), ctypes.POINTER(ctypes.POINTER(IPropertyStore))]
def setWindowProperties(hwnd, app_id, display_name, relaunch_path):
ctypes.windll.ole32.CoInitialize(None)
prop_store = ctypes.POINTER(IPropertyStore)()
result = ctypes.windll.shell32.SHGetPropertyStoreForWindow(int(hwnd), IID_IPropertyStore, ctypes.pointer(prop_store))
if result != 0:
return False
functions = prop_store.contents.lpVtbl.contents
success = False
# PID of PKEY_AppUserModel_ID is 5, etc
values = (5, app_id), (4, display_name), (2, relaunch_path)
for pid, value in values:
prop_key = PROPERTYKEY()
prop_key.fmtid = PKEY_AppUserModel
prop_key.pid = pid
prop_variant = PROPVARIANT()
prop_variant.vt = 31 # VT_LPWSTR
prop_variant.pszVal = value
result = functions.SetValue(prop_store, prop_key, prop_variant)
if result != 0:
break
else:
success = True
if success:
functions.Commit(prop_store)
functions.Release(prop_store)
ctypes.windll.ole32.CoUninitialize()
return success
hwnd = ...
app_id = "CompanyName.ProductName.Version"
display_name = "ProductName"
relaunch_path = "C:\\path\\to\\exe"
setWindowProperties(hwnd, app_id, display_name, relaunch_path)
Upvotes: 0