感谢世卫组织提供了一些提示。实际上,我无法修复DLL,因为我没有源代码。用C++包装它是一种选择,但用C开发包装Python模块听起来更好。
我的计划是只使用Python,可能没有额外的模块,所以我设法只使用ctypes来解决这个问题。以下代码显示了解决方案。它可以工作,但需要一些改进(错误检查等)。
'''
Simple example of how to use the DLL from Python on Win32.
We need only ctypes.
'''
import ctypes
from ctypes import *
'''
We need a class to mirror GUID structure
'''
class GUID(Structure):
_fields_ = [("Data1", c_ulong),
("Data2", c_ushort),
("Data3", c_ushort),
("Data4", c_ubyte * 8)]
if __name__ == "__main__":
'''
COM APIs to activate/deactivate COM environment and load the COM object
'''
ole32=WinDLL('Ole32.dll')
CoInitialize = ole32.CoInitialize
CoUninitialize = ole32.CoUninitialize
CoCreateInstance = ole32.CoCreateInstance
'''
COM environment initialization
'''
rc = CoInitialize(None)
'''
To use CoCreate Instance in C (not C++):
void * driver = NULL;
rc = CoCreateInstance(&IID_Driver, // CLSID of the COM object
0, // no aggregation
CLSCTX_INPROC_SERVER, // CLSCTX_INPROC_SERVER = 1
&IID_Driver, // CLSID of the required interface
(void**)&driver); // result
In Python it is:
'''
clsid = GUID(0x11111111, 0x2222, 0x3333,
(0x44, 0x44, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55))
drv = c_void_p(None)
rc = CoCreateInstance(byref(clsid), 0, 1, byref(clsid), byref(drv))
'''
Pointers manipulation. Short form:
function = cast( c_void_p( cast(drv, POINTER(c_void_p))[0] ), POINTER(c_void_p))
'''
VTable = cast(drv, POINTER(c_void_p))
wk = c_void_p(VTable[0])
function = cast(wk, POINTER(c_void_p))
#print('VTbale address: ', hex(VTable[0]))
#print('QueryInterface address: ', hex(function[0]))
#print('AddRef address: ', hex(function[1]))
#print('Release address: ', hex(function[2]))
'''
To define functions from their addresses we first need to define their WINFUNCTYPE.
In C we call QueryInterface:
HRESULT rc = driver->lpVtbl->QueryInterface(driver, &IID_IUnknown, (void**)&iUnk);
So we need a long (HRESULT) return value and three pointers. It would be better to be
more accurate in pointer types, but ... it works!
We can use whatever names we want for function types and functions
'''
QueryInterfaceType = WINFUNCTYPE(c_long, c_void_p, c_void_p, c_void_p)
QueryInterface = QueryInterfaceType(function[0])
AddRefType = WINFUNCTYPE(c_ulong, c_void_p)
AddRef = AddRefType(function[1])
ReleaseType = WINFUNCTYPE(c_ulong, c_void_p)
Release = ReleaseType(function[2])
'''
The same for other functions, but library functions do not want 'this':
long rc = driver->lpVtbl->init(0);
'''
doThisType = WINFUNCTYPE(c_long, c_void_p)
doThis=doThisType(function[3])
getNameType = WINFUNCTYPE(c_int, c_char_p)
getName = getNameType(function[4])
getName.restype = None # to have None since function is void
getVersionType = WINFUNCTYPE(c_long)
getVersion = getVersionType(function[5])
getMessageType = WINFUNCTYPE(c_int, c_char_p)
getMessage = getMessageType(function[6])
getMessage.restype = None # to have None since function is void
'''
Now we can use functions in plain Python
'''
rc = doThis(0)
print(rc)
name = create_string_buffer(128)
rc = getName(name)
print(rc)
print(name.value)
ver = getVersion()
print(ver)
msg = create_string_buffer(256)
rc = getMessage(msg)
print(rc)
print(msg.value)
'''
Unload DLL and reset COM environment
'''
rc = Release(drv)
rc = CoUninitialize()
print("Done!")
我希望这个例子对某些人有用。它可以用来包装没有comtypes的COM对象,对我来说,还可以阐明ctypes是如何工作的。