我正在尝试为我的python应用程序使用“PortableDevice.PortableDevice”COM API。当我尝试生成python包装器时如下:
comtypes.client.GetModule("C:\\Windows\\system32\\PortableDeviceApi.dll")
我收到以下错误消息:
assert sizeof(__MIDL_IOleAutomationTypes_0004) == 16, sizeof(__MIDL_IOleAutomationTypes_0004)
AssertionError: 8
有人可以帮我解决这个问题吗?
答案 0 :(得分:1)
此失败的主要原因是a kludge in comtypes,其中DECIMAL
类型未正确定义。对于双浮点,它需要64位或8个字节,但对于实际的struct
,它实际上需要16个字节或128位。
对于您目前的目的,您可以使用具有适当大小的DECIMAL
的任何定义,所以这里是一个:
# comtypes/automation.py
class tagDEC(Structure):
_fields_ = [("wReserved", c_ushort),
("scale", c_ubyte),
("sign", c_ubyte),
("Hi32", c_ulong),
("Lo64", c_ulonglong)]
DECIMAL = tagDEC
# comtypes/tools/tlbparser.py
DECIMAL_type = typedesc.Structure("DECIMAL",
align=alignment(automation.DECIMAL)*8,
members=[], bases=[],
size=sizeof(automation.DECIMAL)*8)
但是,您可能会遗憾的是,便携式设备API中的某些方法不适合自动化。
例如,IPortableDeviceManager::GetDevices
具有unique
attribute(在实际的PortableDeviceApi.idl文件中,而不在文档中),这意味着您实际上可以传递NULL
。但是,type libraries don't capture this information。
同样的参数实际上可以是一个数组,其大小由下一个参数决定。同样,类型库不支持这一点,只支持单对象顶级指针。此外,实际IDL没有size_is
attribute,这意味着方法调用不能在公寓之间工作,或者此接口具有自定义封送器。
快速浏览一下Portable Device API,可以看出这种模式一直应用于其他实际使用数组的方法。似乎熟悉Win32 API的人制作了这些方法,因为当数组参数为NULL
时,有一堆Win32函数被重载以获取某些数组的大小。这根本不是通常的COM方式,最好有两个方法(在知道元素数量,分配足够的内存和获取它们之间具有相同的竞争条件),只有out
个参数的单个方法(没有竞争条件,但无法控制内存使用情况)或使用枚举器(例如IEnumPortableDevice
,更难,但更清洁)。
无论如何,您可以将comtypes.client.GetModule("…PortableDeviceApi.dll")
生成的代码作为第一步。然后,按照these instructions使Python方法实际上以记录的方式调用COM方法。例如,IPortableManager::GetDevices
将成为:
# comtypes/gen/_1F001332_1A57_4934_BE31_AFFC99F4EE0A_0_1_0.py
class IPortableDeviceManager(comtypes.gen._00020430_0000_0000_C000_000000000046_0_2_0.IUnknown):
# ...
def GetDevices(self):
cPnPDeviceIDs = c_ulong(0)
self.__com_GetDevices(None, byref(cPnPDeviceIDs))
PnPDeviceIDs = (WSTRING * cPnPDeviceIDs.value)()
self.__com_GetDevices(PnPDeviceIDs, byref(cPnPDeviceIDs))
deviceIDs = PnPDeviceIDs[:cPnPDeviceIDs.value]
for i in range(cPnPDeviceIDs.value):
windll.ole32.CoTaskMemFree(cast(PnPDeviceIDs, POINTER(c_void_p))[i])
return deviceIDs
# ...
IPortableDeviceManager._methods_ = [
COMMETHOD([], HRESULT, 'GetDevices',
( ['in'], POINTER(WSTRING), 'pPnPDeviceIDs' ),
( ['in'], POINTER(c_ulong), 'pcPnPDeviceIDs' )),
# ...
以下测试成功运行,虽然它在我的情况下返回一个空列表,因为我现在没有任何连接设备:
# First run
import os
from comtypes.client import GetModule
GetModule(os.getenv("WINDIR") + "\\system32\\PortableDeviceApi.dll")
# Quit python
# Edit comtypes/gen/_1F001332_1A57_4934_BE31_AFFC99F4EE0A_0_1_0.py
# Subsequent runs
from comtypes.client import CreateObject
from comtypes.gen.PortableDeviceApiLib import *
CreateObject(PortableDeviceManager).GetDevices()
如果不进一步了解comtypes,我无法提供更多帮助。我建议你联系它的作者或维护者。
编辑:同时,我在SourceForge网站created a ticket。由于该项目正在从SourceForge过渡,似乎忘记了but it wasn't(here too)。
从那以后你有没有尝试导入模块?