从Python访问COM方法 [英] Access COM methods from Python

查看:81
本文介绍了从Python访问COM方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个没有源代码的旧Windows DLL,可实现实用程序功能表.几年前,它计划将其转换为COM对象,因此实现了IUnknown接口.要使用此DLL,有一个头文件(简体):

I have an old Windows DLL, without source code, who implement a table of utility functions. Years ago it was planned to convert it in a COM object so an IUnknown interface was implemented. To work with this DLL, there is a header file (simplified):

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

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

But no CLSID was defined for IFunctions interface. And eventually the interface definition in header file is non-compliant with COM standard.

从C ++可以用

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

并使用'ptr'中的一些指针算法,我找到了funcion1()等的地址.由于它起作用,因此未完成任何完整的COM实现,因此我无法为IFunctions接口使用QueryInterface,因为该接口不是COM接口.在Windows注册表中,我仅找到对象的CLSID和对InprocServer32的DLL的引用.

and with some pointer arithmetic from 'ptr' I find the addresses of funcion1(), etc. Since it worked, no complete COM implementation were done so I cannot QueryInterface for IFunctions interface because the interface is not a COM interface. In Windows Registry I find only the CLSID of the object and a reference to the DLL as it InprocServer32.

我在Python方面没有太多经验,但是我需要使用Python中的这个DLL,也许使用ctypes和comtypes.我可以使用(注册表中的CLSID)加载DLL

I do not have much experience in Python, but I need to use this DLL from Python, perhaps using ctypes and comtypes. I can load the DLL with (CLSID from registry)

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

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

I know that in the VTable of the COM object function1() address is just after QueryInterface(), AddRef(), Release() but I cannot find a solution to implement a class like:

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 ++扩展模块.

I would like to implement this solution in Python trying to avoid the development of an extension module in C++.

感谢您的帮助.

推荐答案

感谢谁提供了一些提示.实际上,我无法修复DLL,因为我没有源代码.用C ++包装它是一种选择,但是用C开发包装的Python模块听起来更好.我的计划是仅使用Python,可能不使用其他模块,因此我设法仅使用ctypes解决了该问题.以下代码显示了解决方案.它可以工作,但是需要一些改进(错误检查等).

Thanks to who provided some hints. Actually I cannot fix the DLL because I do not have the source code. Wrapping it in C++ was an option, but developing a wrapping Python module in C sounds better. My plan was to use Python only, possibly without additional modules, so I managed to solve the issue using only ctypes. The following code show the solution. It works, but it needs some improvements (error checking, etc).

'''
    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的工作原理.

I hope this example will be useful to somebody. It can be used to wrap a COM object without comtypes and, to me, clarify how ctypes works.

这篇关于从Python访问COM方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆