如何自省win32com包装器? [英] How to introspect win32com wrapper?

查看:111
本文介绍了如何自省win32com包装器?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个设备,该设备可以记录光谱数据并由第三方应用程序控制.为了实现自动化,我想使用应用程序的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中的定义不一致:

Sub GetFrame(frame As Integer, buffer As Variant)

GetFrame将数据从文档复制到Visual Basic数组中.如果buffer是一个空的Variant,则GetFrame将创建一个具有适当大小和数据类型的数组,并在复制数据之前将缓冲区设置为指向该数组.

这意味着在Visual Basic中,变量buffer被数据填充,而函数GetFrame没有返回值,而在Python中,buffer保持不变,但函数GetFrame确实返回了实际数据. /p>

如果我没有观察到程序抛出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包装程序的更多信息吗?

解决方案

通过更多尝试,我终于能够找到解决问题的方法.第一个重要的发现是发现,调用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邮件列表上的消息,InvokeTypesInvoke非常相似,后者又是 .可以在博客条目,相应的第一个数字告诉我们可以使用 Variant 数据类型.

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

16396 = 0x400c = VT_VARIANT | VT_BYREF

文档告诉我们VT_VARIANT的实际含义.

指定的类型,元素的类型或所包含字段的类型必须为VARIANT

不是超级有启发性,但仍然如此.似乎通过ctypes.c_float的选择实际上并不是一个好选择.取而代之的是,我现在正在传递一种变体,可能应该受到讨论.

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

由于进行了此更改,因此我不再观察到此代码部分的崩溃,因此原来的问题已为我解决.

I have a device, which records spectroscopic data and is controlled by a 3rd-party application. For automization purposes, I want to use the COM interface of the application to retrieve the data in Python. Since there is no proper documentation for using the API from Python, I collected the following code from different web sources, which successfully obtains the first frame:

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)

However, the call to GetFrame is inconsistent with its definition in Visual Basic, which is provided by the manufacturer:

Sub GetFrame(frame As Integer, buffer As Variant)

GetFrame copies the data from a document into a Visual Basic array. If buffer is an empty Variant, GetFrame creates an array of the proper size and data type and sets buffer to point to it before copying the data.

This means that in Visual Basic the variable buffer is filled with data while the function GetFrame has no return value, whereas in Python buffer remains unchanged but the function GetFrame does return the actual data.

I wouldn't care about such subtleties, if I hadn't observed random crashes of my program throwing a MemoryError and thus indicating a memory leak at this very point of the code. So my suspicion is that for each call to GetFrame some memory is allocated for buffer but never released, because win32com somehow messed up the API wrapping.

That reasoning leads me to my actual question: How can I introspect that wrapper and understand what it does? So far, I could not find any hints that the code generated by win32com is stored in any file, but maybe I just have not been looking at the right places.

In IPython I also tried to get information using doc.GetFrame??, but it did not return any implementation:

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

What else can I try to get more information about the API wrapper?

解决方案

Trying around more, I was finally able to find a solution to my problem. The first important realization was the finding that calling EnsureDispatch instead of Dispatch gives me access to the wrapper generated by win32com.

>>> import win32com.client
>>> doc = win32com.client.gencache.EnsureDispatch ("WinX32.DocFile")
>>> print(doc.GetFrame.__module__)
'win32com.gen_py.1A762221-D8BA-11CF-AFC2-508201C10000x0x3x12.IDocFile4'

In my case the corresponding file was located in the following folder:

C:\WinPython\WinPython-32bit-3.5.2.2\python-3.5.2\Lib\site-packages\win32com\gen_py\1A762221-D8BA-11CF-AFC2-508201C10000x0x3x12

The implementation of GetFrame looks as follows.

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)

So the magic is in method _ApplyTypes_. This method itself is defined in 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)

We can see that everything is basically passed to InvokeTypes. According to this message on the Python-win32 mailing list, InvokeTypes is very similar to Invoke, which in turn is a re-implementation of IDispatch::Invoke. The source code of the C++ implementation integrated in Python can be found here.

Going through this C++ implementation also explains, what bothered me in my original question: The Python version of Invoke explicitly turns byref arguments into return values. Hence, at least, there should be no memory leak, which I suspected in the beginning.

Now what can we learn about the argument types? The necessary information is stored in the tuple ((2, 1), (16396, 3)). We have two arguments, of which the first is an input only argument (indicated by 1), while the second is an input and output argument (indicated by 3 = 1 | 2). According to this blog entry, the respective first numbers tell us the kind of Variant datatype that is expected.

We can look up in this list, what the numbers actually mean. The first argument is a signed int16, which makes sense, since it specifies the frame number. The second number has the following meaning.

16396 = 0x400c = VT_VARIANT | VT_BYREF

The documentation tells us, what VT_VARIANT actually means.

Either the specified type, or the type of the element or contained field MUST be VARIANT

Not super instructive, but still. It seems that the choice to pass a ctypes.c_float is not really a good choice. Instead, I am now passing a variant, as I probably should, inspired by this discussion.

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

Since making this change, I have no longer observed crashes of this code part, so the original question is solved for me.

这篇关于如何自省win32com包装器?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆