感谢提供一些提示的人。实际上,我无法修复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))
'''
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
getVersionType = WINFUNCTYPE(c_long)
getVersion = getVersionType(function[5])
getMessageType = WINFUNCTYPE(c_int, c_char_p)
getMessage = getMessageType(function[6])
getMessage.restype = None
'''
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的工作原理。