2016-11-17 91 views
1

我有一个设备,它记录光谱数据,并由第三方应用程序控制。为了实现自动化,我想使用应用程序的COM接口来检索Python中的数据。由于没有用于使用从Python中API没有适当的文档,我收集来自不同网络资源下面的代码,它成功地获得第一帧:如何反思win32com包装?

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是与其在视觉定义不一致基本的,这是由制造商提供:

Sub GetFrame(frame As Integer, buffer As Variant) 

GetFrame拷贝从一个文档的数据到Visual Basic阵列。如果buffer是空变量,则GetFrame将创建一个具有适当大小和数据类型的数组,并在复制数据之前将缓冲区设置为指向它。

这意味着,在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 

我还能尝试获得有关API的封装器的更多信息?

回答

1

尝试更多,我终于能够找到解决我的问题。第一个重要的实现是发现调用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) 

所以魔法是在方法_ApplyTypes_。该方法本身在win32com\client\__init__中定义。

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邮件列表中的this消息,InvokeTypesInvoke非常相似,后者又是IDispatch::Invoke的重新实现。 Python中集成的C++实现的源代码可以在here找到。

通过这个C++实现还解释了,在我原来的问题中,困扰我的是什么:Invoke的Python版本明确地将byref参数转换为返回值。因此,至少应该没有内存泄漏,我一开始就怀疑这种泄漏。

现在我们可以了解什么参数类型?必要的信息存储在元组((2, 1), (16396, 3))中。我们有两个参数,其中第一个是仅输入参数(由1指示),而第二个参数是输入和输出参数(由3 = 1 | 2指示)。根据this的博客条目,相应的第一个数字告诉我们预期的那种Variant数据类型。

我们可以在this列表中查看,这些数字实际上是什么意思。第一个参数是一个有符号的int16,这是有道理的,因为它指定了帧号。第二个数字具有以下含义。

16396 = 0x400c = VT_VARIANT | VT_BYREF 

documentation告诉我们,什么VT_VARIANT实际上意味着。

要么指定的类型,或者该元素的类型或包含字段必须是VARIANT

不是超级启发,但仍。看起来通过ctypes.c_float的选择并不是一个好的选择。相反,我现在正在通过一个变体,因为我可能应该从this的讨论中获得启发。

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

由于进行此更改,我不再观察此代码部分的崩溃,因此原始问题已解决。