仅使用 ctypes 通过 SHGetPropertyStoreForWindow 设置窗口属性

问题描述 投票:0回答:1

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 的依赖,但有关实现此功能的信息很少。

python winapi ctypes
1个回答
0
投票
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)
© www.soinside.com 2019 - 2024. All rights reserved.