ran*_*wud 3 python introspection win32com
我有一个设备,它记录光谱数据并由第 3 方应用程序控制。出于自动化目的,我想使用应用程序的 COM 接口来检索 Python 中的数据。由于没有使用 Python 的 API 的适当文档,我从不同的网络资源中收集了以下代码,成功获得了第一帧:
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)
Run Code Online (Sandbox Code Playgroud)
但是,对 的调用GetFrame与它在 Visual Basic 中的定义不一致,由制造商提供:
Run Code Online (Sandbox Code Playgroud)Sub GetFrame(frame As Integer, buffer As Variant)
GetFrame将文档中的数据复制到 Visual Basic 数组中。如果buffer是空 Variant,则GetFrame创建一个具有适当大小和数据类型的数组,并在复制数据之前将缓冲区设置为指向它。
这意味着在 Visual Basic 中,变量buffer填充了数据,而函数GetFrame没有返回值,而在 Python 中buffer保持不变,但函数GetFrame确实返回了实际数据。
如果我没有观察到我的程序随机崩溃抛出 aMemoryError并因此在代码的这一点指示内存泄漏,我就不会关心这些微妙之处。所以我的怀疑是,每次调用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
Run Code Online (Sandbox Code Playgroud)
我还可以尝试获取有关 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'
Run Code Online (Sandbox Code Playgroud)
在我的情况下,相应的文件位于以下文件夹中:
C:\WinPython\WinPython-32bit-3.5.2.2\python-3.5.2\Lib\site-packages\win32com\gen_py\1A762221-D8BA-11CF-AFC2-508201C10000x0x3x12
Run Code Online (Sandbox Code Playgroud)
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)
Run Code Online (Sandbox Code Playgroud)
所以魔法在于方法_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)
Run Code Online (Sandbox Code Playgroud)
我们可以看到,一切基本上都传递给了InvokeTypes. 根据Python-win32 邮件列表上的这条消息,与InvokeTypes非常相似Invoke,而后者又是IDispatch::Invoke. 可以在此处找到集成在 Python 中的 C++ 实现的源代码。
通过这个 C++ 实现还解释了我最初的问题中困扰我的地方:Python 版本的Invoke显式将 byref 参数转换为返回值。因此,至少应该没有内存泄漏,这是我一开始怀疑的。
现在我们可以从参数类型中学到什么?必要的信息存储在元组中((2, 1), (16396, 3))。我们有两个参数,其中第一个是仅输入参数(由 表示1),而第二个是输入和输出参数(由 表示3 = 1 | 2)。根据此博客条目,相应的第一个数字告诉我们Variant预期的数据类型类型。
我们可以在这个列表中查找这些数字的实际含义。第一个参数是一个 signed int16,这是有道理的,因为它指定了帧号。第二个数字的含义如下。
16396 = 0x400c = VT_VARIANT | VT_BYREF
Run Code Online (Sandbox Code Playgroud)
该文件告诉我们,有什么VT_VARIANT实际意义。
指定的类型,或者元素的类型或包含的字段必须是 VARIANT
不是很有指导意义,但仍然如此。看来选择pass actypes.c_float并不是真正的好选择。相反,我现在正在传递一个变体,我可能应该受到这个讨论的启发。
var = win32com.client.VARIANT(pythoncom.VT_VARIANT | pythoncom.VT_NULL | pythoncom.VT_BYREF, None)
spectrum = doc.GetFrame(frame, var)
Run Code Online (Sandbox Code Playgroud)
自从进行此更改后,我不再观察到此代码部分的崩溃,因此原始问题已为我解决。
| 归档时间: |
|
| 查看次数: |
1167 次 |
| 最近记录: |