如何仅用ctypes实现SHGetPropertyStoreForWindow设置PyQt5窗口任务栏属性
用ctypes实现PyQt5应用Windows任务栏交互属性设置(替代pywin32)
对于PyQt5应用,要让它和Windows任务栏正常交互(正确显示图标、分组、支持固定),可以通过设置窗口的AppUserModel属性实现。下面是不依赖pywin32,纯用ctypes的实现方案:
完整实现代码
import ctypes from ctypes import wintypes # 定义基础类型和常量 CLSCTX_INPROC_SERVER = 0x1 IID_IPropertyStore = ctypes.GUID("{886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99}") IID_IUnknown = ctypes.GUID("{00000000-0000-0000-C000-000000000046}") # PKEY定义,对应AppUserModel相关属性 PKEY_AppUserModel_ID = ctypes.GUID("{9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}", 5) PKEY_AppUserModel_RelaunchDisplayNameResource = ctypes.GUID("{9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}", 11) PKEY_AppUserModel_RelaunchCommand = ctypes.GUID("{9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}", 10) # PROPVARIANT结构定义 class PROPVARIANT(ctypes.Structure): _fields_ = [ ("vt", wintypes.VT), ("wReserved1", wintypes.WORD), ("wReserved2", wintypes.WORD), ("wReserved3", wintypes.WORD), ("union", ctypes.Union( _fields_ = [ ("pwszVal", wintypes.LPWSTR), # 仅实现字符串类型,其他类型可按需扩展 ] )) ] # 加载系统库 shell32 = ctypes.WinDLL("shell32") ole32 = ctypes.WinDLL("ole32") # 声明函数原型 shell32.SHGetPropertyStoreForWindow.restype = wintypes.HRESULT shell32.SHGetPropertyStoreForWindow.argtypes = [wintypes.HWND, ctypes.POINTER(ctypes.GUID), ctypes.POINTER(ctypes.c_void_p)] # IPropertyStore接口定义 class IPropertyStore(ctypes.Structure): _fields_ = [("lpVtbl", ctypes.POINTER("IPropertyStoreVtbl"))] class IPropertyStoreVtbl(ctypes.Structure): _fields_ = [ ("QueryInterface", ctypes.CFUNCTYPE(wintypes.HRESULT, ctypes.POINTER(IPropertyStore), ctypes.POINTER(ctypes.GUID), ctypes.POINTER(ctypes.c_void_p))), ("AddRef", ctypes.CFUNCTYPE(wintypes.ULONG, ctypes.POINTER(IPropertyStore))), ("Release", ctypes.CFUNCTYPE(wintypes.ULONG, ctypes.POINTER(IPropertyStore))), ("GetCount", ctypes.CFUNCTYPE(wintypes.HRESULT, ctypes.POINTER(IPropertyStore), ctypes.POINTER(wintypes.DWORD))), ("GetAt", ctypes.CFUNCTYPE(wintypes.HRESULT, ctypes.POINTER(IPropertyStore), wintypes.DWORD, ctypes.POINTER(ctypes.GUID))), ("GetValue", ctypes.CFUNCTYPE(wintypes.HRESULT, ctypes.POINTER(IPropertyStore), ctypes.POINTER(ctypes.GUID), ctypes.POINTER(PROPVARIANT))), ("SetValue", ctypes.CFUNCTYPE(wintypes.HRESULT, ctypes.POINTER(IPropertyStore), ctypes.POINTER(ctypes.GUID), ctypes.POINTER(PROPVARIANT))), ("Commit", ctypes.CFUNCTYPE(wintypes.HRESULT, ctypes.POINTER(IPropertyStore))) ] def set_window_properties(hwnd, app_id, display_name, relaunch_path): # 初始化COM环境 ole32.CoInitialize(None) try: # 获取IPropertyStore接口指针 prop_store_ptr = ctypes.c_void_p() hr = shell32.SHGetPropertyStoreForWindow(hwnd, ctypes.byref(IID_IPropertyStore), ctypes.byref(prop_store_ptr)) if hr != 0: raise ctypes.WinError(hr) prop_store = ctypes.cast(prop_store_ptr, ctypes.POINTER(IPropertyStore)) # 辅助函数:设置字符串类型属性 def set_string_prop(pkey, value): pv = PROPVARIANT() pv.vt = wintypes.VT_LPWSTR pv.union.pwszVal = ctypes.c_wchar_p(value) hr = prop_store.contents.lpVtbl[6](prop_store, ctypes.byref(pkey), ctypes.byref(pv)) if hr != 0: raise ctypes.WinError(hr) # 设置任务栏相关属性 set_string_prop(PKEY_AppUserModel_ID, app_id) set_string_prop(PKEY_AppUserModel_RelaunchDisplayNameResource, display_name) set_string_prop(PKEY_AppUserModel_RelaunchCommand, relaunch_path) # 提交属性更改 hr = prop_store.contents.lpVtbl[7](prop_store) if hr != 0: raise ctypes.WinError(hr) # 释放接口引用 prop_store.contents.lpVtbl[2](prop_store) finally: # 反初始化COM环境 ole32.CoUninitialize() # PyQt5调用示例 if __name__ == "__main__": from PyQt5.QtWidgets import QApplication, QMainWindow import sys app = QApplication(sys.argv) window = QMainWindow() window.show() # 获取窗口句柄 hwnd = int(window.winId()) app_id = "CompanyName.ProductName.Version" display_name = "ProductName" relaunch_path = "C:\\path\\to\\your\\app.exe" set_window_properties(hwnd, app_id, display_name, relaunch_path) sys.exit(app.exec_())
关键说明
- COM环境管理:Windows属性系统依赖COM,必须调用
CoInitialize和CoUninitialize完成环境的初始化与清理。 - 窗口句柄获取:PyQt5中通过
QWidget.winId()获取窗口句柄,转换为整数后传入函数即可。 - 属性参数要求:
app_id:应用唯一标识,建议格式为公司名.产品名.版本,是任务栏分组、固定功能的核心依据。display_name:应用固定到任务栏后显示的名称。relaunch_path:固定后点击任务栏图标重启应用的完整路径。
- PROPVARIANT扩展:代码仅实现了字符串类型的属性赋值,若需设置其他类型属性,可扩展
PROPVARIANT的union字段。
内容的提问来源于stack exchange,提问作者arenasys
相关产品推荐
相关产品推荐

