使用comtypes.client.GetModule()为dl​​l文件生成python包装时的AssertionError

时间:2014-01-23 07:06:38

标签: python com comtypes

我正在尝试为我的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

有人可以帮我解决这个问题吗?

1 个答案:

答案 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'there too)。

从那以后你有没有尝试导入模块?