gpt4 book ai didi

python-3.x - 从 Python 访问 COM 方法

转载 作者:行者123 更新时间:2023-12-04 03:01:16 24 4
gpt4 key购买 nike

我有一个旧的 Windows DLL,没有源代码,它实现了一个实用函数表。多年前,计划将其转换为 COM 对象,因此实现了 IUnknown 接口(interface)。要使用此 DLL,有一个头文件(已简化):

interface IFunctions : public IUnknown
{
virtual int function1(int p1, int p2) = 0;
virtual void function2(int p1) = 0;
// and the likes ...
}

但是没有为 IFunctions 接口(interface)定义 CLSID。最终头文件中的接口(interface)定义不符合COM标准。

可以从 C++ 加载 DLL

CoCreateInstance(clsid, 0, CLSCTX_INPROC_SERVER, clsid, ptr);

通过'ptr' 中的一些指针算法,我找到了 funcion1() 等的地址。由于它有效,因此没有完成完整的 COM 实现,所以我不能为 IFunctions 接口(interface)查询接口(interface),因为该接口(interface)不是 COM 接口(interface)。在 Windows 注册表中,我只找到对象的 CLSID 和对 DLL 的引用,因为它是 InprocServer32。

我在 Python 方面没有太多经验,但我需要从 Python 使用这个 DLL,可能使用 ctypes 和 comtypes。我可以使用(来自注册表的 CLSID)加载 DLL

unk = CreateObject('{11111111-2222-3333-4444-555555555555}', clsctx=comtypes.CLSCTX_INPROC_SERVER)

我知道在 COM 对象的 VTable 中 function1() 地址就在 QueryInterface()、AddRef()、Release() 之后,但我找不到实现类的解决方案:

class DllFunction:
# not necessary, but for completeness ...
def QueryInterface(self, interface, iid=None):
return unk.QueryInterface(comtypes.IUnknown)
def AddRef(slef):
return unk.AddRef()
def Release(self):
return unk.Release()
# Functions I actually need to call from Python
def Function1(self, p1, p2):
# what to do ??
def Function2(self, p1):
# etc.

我想在 Python 中实现这个解决方案,试图避免在 C++ 中开发扩展模块。

感谢您的帮助。

最佳答案

感谢谁提供了一些提示。实际上我无法修复 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 的工作原理。

关于python-3.x - 从 Python 访问 COM 方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48986244/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com