Python 如何反思win32com包装器?

Python 如何反思win32com包装器?,python,introspection,win32com,Python,Introspection,Win32com,我有一个设备,它记录光谱数据,并由第三方应用程序控制。出于自动化的目的,我想使用应用程序的COM接口来检索Python中的数据。由于没有使用Python API的适当文档,我从不同的web源中收集了以下代码,成功地获得了第一帧: comtypes.client.GetModule(('{1A762221-D8BA-11CF-AFC2-508201C10000}', 3, 11)) import comtypes.gen.WINX32Lib as WinSpecLib win32com.clien

我有一个设备,它记录光谱数据,并由第三方应用程序控制。出于自动化的目的,我想使用应用程序的COM接口来检索Python中的数据。由于没有使用Python API的适当文档,我从不同的web源中收集了以下代码,成功地获得了第一帧:

comtypes.client.GetModule(('{1A762221-D8BA-11CF-AFC2-508201C10000}', 3, 11))
import comtypes.gen.WINX32Lib as WinSpecLib
win32com.client.pythoncom.CoInitialize()
doc = win32com.client.Dispatch("WinX32.DocFile")

buffer = ctypes.c_float()
frame = 1
spectrum = doc.GetFrame(frame, buffer)
但是,对
GetFrame
的调用与制造商提供的Visual Basic中的定义不一致:

GetFrame
将文档中的数据复制到Visual Basic数组中。如果
buffer
是空变量,
GetFrame
创建一个大小和数据类型正确的数组,并在复制数据之前将buffer设置为指向该数组

这意味着在Visual Basic中,变量
buffer
充满数据,而函数
GetFrame
没有返回值,而在Python中,buffer保持不变,但函数
GetFrame
返回实际数据

如果我没有观察到我的程序随机崩溃,抛出
MemoryError
,从而在代码的这一点上指示内存泄漏,我就不会关心这些微妙之处。因此,我怀疑,对于
GetFrame
的每次调用,都会为缓冲区分配一些内存,但从未释放,因为
win32com
以某种方式破坏了API包装

这一推理引出了我的实际问题:我如何反思包装器并理解它的功能?到目前为止,我找不到任何提示,说明由
win32com
生成的代码存储在任何文件中,但可能我只是没有找到正确的位置

在IPython中,我还尝试使用
doc.GetFrame???
获取信息,但它没有返回任何实现:

Signature: doc.GetFrame(frame=<PyOleMissing object at 0x06F20BC8>, FrameVariant=<PyOleMissing object at 0x06F20BC8>)
Docstring: <no docstring>
File:      c:\programming\python\src\<comobject winx32.docfile>
Type:      method
签名:doc.GetFrame(frame=,FrameVariant=) 文档字符串: 文件:c:\programming\python\src\ 类型:方法
我还可以尝试获取关于API包装器的更多信息吗?

尝试了更多,我终于找到了解决问题的方法。第一个重要的实现是发现调用
EnsureDispatch
而不是
Dispatch
可以访问
win32com
生成的包装

>>> import win32com.client
>>> doc = win32com.client.gencache.EnsureDispatch ("WinX32.DocFile")
>>> print(doc.GetFrame.__module__)
'win32com.gen_py.1A762221-D8BA-11CF-AFC2-508201C10000x0x3x12.IDocFile4'
在我的情况下,相应的文件位于以下文件夹中:

C:\WinPython\WinPython-32bit-3.5.2.2\python-3.5.2\Lib\site-packages\win32com\gen_py\1A762221-D8BA-11CF-AFC2-508201C10000x0x3x12
GetFrame
的实现如下所示

def GetFrame(self, frame=defaultNamedNotOptArg, FrameVariant=defaultNamedNotOptArg):
    'Get Frame Data'
    return self._ApplyTypes_(10, 1, (24, 0), ((2, 1), (16396, 3)), 'GetFrame', None, frame, FrameVariant)
因此,神奇之处在于方法
\u ApplyTypes\u
。此方法本身在
win32com\client\\uuuu init\uuuu
中定义

def _ApplyTypes_(self, dispid, wFlags, retType, argTypes, user, resultCLSID, *args):
    return self._get_good_object_(
        self._oleobj_.InvokeTypes(dispid, 0, wFlags, retType, argTypes, *args),
        user, resultCLSID)
我们可以看到,所有内容基本上都传递给
InvokeTypes
。根据Python-win32邮件列表上的消息,
InvokeTypes
Invoke
非常相似,这反过来又是对的重新实现。在Python中集成的C++实现源代码可以找到。

通过C++实现也解释了困扰我的问题:Python版本的代码>调用KEKE/<代码>显式地将BYRF参数转换为返回值。因此,至少应该没有内存泄漏,这是我在一开始就怀疑的

现在我们能从参数类型中学到什么?必要的信息存储在元组
((2,1)、(16396,3))
中。我们有两个参数,第一个是纯输入参数(由
1
表示),第二个是输入和输出参数(由
3=1 | 2
表示)。根据博客条目,相应的第一个数字告诉我们预期的数据类型

我们可以在列表中查找这些数字的实际含义。第一个参数是有符号的
int16
,这很有意义,因为它指定了帧编号。第二个数字的含义如下

16396 = 0x400c = VT_VARIANT | VT_BYREF
它告诉我们,VT_变体的实际含义是什么

指定的类型、元素的类型或包含的字段必须是VARIANT

不是很有教育意义,但仍然是。似乎传递
ctypes.c_float
的选择并不是一个好的选择。相反,我现在通过了一个变体,我可能应该这样做,它受到了讨论的启发

var = win32com.client.VARIANT(pythoncom.VT_VARIANT | pythoncom.VT_NULL | pythoncom.VT_BYREF, None)
spectrum = doc.GetFrame(frame, var)
自从做了这个更改之后,我再也没有观察到这个代码部分的崩溃,所以原来的问题为我解决了

var = win32com.client.VARIANT(pythoncom.VT_VARIANT | pythoncom.VT_NULL | pythoncom.VT_BYREF, None)
spectrum = doc.GetFrame(frame, var)