Python 开发人员需要为其应用程序设置某些窗口属性,以便与 Windows 任务栏正确配合(正确的图标和分组,以及固定的能力)。就我而言,是 PyQt5 应用程序。
可以使用 pywin32 访问属性存储,如下所示:
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)
理论上,这可以仅使用 ctypes 来完成,以避免对 pywin32 的依赖,但有关实现此功能的信息很少。
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))]
IID_IPropertyStore = (GUID)(*bytearray.fromhex("eb8e6d88f28c46448d02cdba1dbdcf99"))
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
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
prop_variant.pszVal = value
result = functions.SetValue(prop_store, prop_key, prop_variant)
if result != 0:
break
else:
functions.Commit(prop_store)
functions.Release(prop_store)
return True
functions.Release(prop_store)
return False
hwnd = ...
app_id = "CompanyName.ProductName.Version"
display_name = "ProductName"
relaunch_path = "C:\\path\\to\\exe"
setWindowProperties(hwnd, app_id, display_name, relaunch_path)