2016-11-17 22 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) 

由於進行此更改,我不再觀察此代碼部分的崩潰,因此原始問題已解決。