通过注册的TLB从python访问未注册的COM对象 [英] Accessing unregistered COM objects from python via a registered TLB

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

问题描述

我有三个代码,我目前与工作:




  • 一个封闭的源代码应用程序

  • 以dll(comobj.dll)实现的关闭源VB COM对象

  • 在Python中开发的代码



comobj.dll托管一个我想从Python中使用的COM对象(假设为'MainInteract')。我已经可以使用这个对象完全FinePython,但由于其他要求,我需要使用它从普通的Python。我相信最好的方法是使用win32com,但我根本不能取得任何进展。



首先,一些工作IronPython代码:

  import clr 
import os
import sys

__dir__ = os.path.dirname(os .path.realpath(__ file__))
sys.path.insert(0,__dir__)
sys.path.append(rC:\Path\To\comobj.dll)#这是com对象dll实际上是

clr.AddReferenceToFileAndPath(os.path.join(__ dir__,r'comobj_1_1.dll'))#这是通过自动创建的.NET互操作程序集SharpDevelop的COM检查器

来自comobj_1_1 import clsMainInteract

o = clsMainInteract()
o.DoStuff(True)






现在我在常规Python中尝试的代码:

 >>> import win32com.client 
>>>> win32com.client.Dispatch({11111111-comobj_guid_i_got_from_com_inspector})
回溯(最近一次调用):
在< module>中第1行的文件< stdin&
文件C:\Python26\lib\site-packages\win32com\client\__init __。py,第95行,在Dispatch
dispatch中,userName = dynamic._GetGoodDispatchAndUserName(dispatch ,userName,clsctx)
文件C:\Python26\lib\site-packages\win32com\client\dynamic.py,第104行,位于_GetGoodDispatchAndUserName
return(_GetGoodDispatch IDispatch,clsctx),userName)
文件C:\Python26\lib\site-packages\win32com\client\dynamic.py,第84行,位于_GetGoodDispatch
IDispatch = pythoncom.CoCreateInstance(IDispatch,None,clsctx,pythoncom.IID_IDispatch)
pywintypes.com_error:(-2147221164,'Class not registered',None,None)

我也尝试使用TLB的友好名称:

 >>>> import win32com.client 
>>>> win32com.client.Dispatch(Friendly TLB Name I Saw)
回溯(最近最后一次调用):
在< module>中第1行的文件< stdin&
文件C:\Python26\lib\site-packages\win32com\client\__init __。py,第95行,在Dispatch
dispatch中,userName = dynamic._GetGoodDispatchAndUserName(dispatch ,userName,clsctx)
文件C:\Python26\lib\site-packages\win32com\client\dynamic.py,第104行,位于_GetGoodDispatchAndUserName
return(_GetGoodDispatch IDispatch,clsctx),userName)
文件C:\Python26\lib\site-packages\win32com\client\dynamic.py,第84行,位于_GetGoodDispatch
IDispatch = pythoncom.CoCreateInstance(IDispatch,None,clsctx,pythoncom.IID_IDispatch)
pywintypes.com_error:(-2147221005,'无效的类字符串',无,无)

事实上,我唯一的成功是:

  import pythoncom 
tlb = pythoncom.LoadRegTypeLib({11111111-comobj_guid_i_got_from_com_inspector},1,1,0)
>>> tlb
< PyitypeLib at 0x00AD7D78 with obj at 0x0025EDF0>
>>>> tlb.GetDocumentation(1)
(u'clsMainInteract',None,0,None)

但我不知道如何从那里去得到一个对象。我想我的问题是,我需要加载dll到我的进程,并让它注册自己与我的进程的COM源,所以我可以正确地CoCreateInstance / win32com.client.Dispatch()。



我也看过激活上下文,特别是当谈到无注册COM,但通常在句子中如果你指定正确的东西在你的.manifest文件中,Windows将创建一个上下文。我想避免清单文件,如果可能的话,在同一文件夹中需要与(闭源)COM对象dll,我宁愿不删除该目录中的任何文件,如果我可以避免它。 p>

感谢您的帮助。

解决方案

对象从DLL的情况以及其他情况,请参见 https://gist.github.com/4219140

  __ all__ =(
#######类对象

#CoGetClassObject - Normal,not wrapped
'CoDllGetClassObject',#Get ClassObject from a DLL file

####### ClassFactory :: CreateInstance Wrappers

'CoCreateInstanceFromFactory',#通过IClassFactory :: CreateInstance
'CoCreateInstanceFromFactoryLicenced'创建一个对象,#通过IClassFactory2 :: CreateInstanceLic创建一个许可对象

###### Util

'CoReleaseObject',#Calls对COM对象的Release()

######主要实用方法

#'CoCreateInstance',#无,正常调用
'CoCreateInstanceLicenced',#CoCreateInstance,但是带有许可证密钥

###### Hacky DLL方法用于无注册COM而没有激活上下文,清单等
'CoCreateInstanceFromDll',#Given a dll,clsid和iid,创建一个对象
'CoCreateInstanceFromDllLicenced',#Given a dll,clsid,iid和许可证密钥,创建一个对象


IID_IClassFactory2 ={B196B28F-BAB4-101A-B69C-00AA00341D07}

来自uuid import UUID
来自ctypes import OleDLL,WinDLL,c_ulong ,byref,WINFUNCTYPE,POINTER,c_char_p,c_void_p
来自ctypes.wintypes import HRESULT
import pythoncom
import win32com.client

import logging
log = logging.getLogger(__ name__)


def _raw_guid(guid):
给定一个字符串GUID或者pythoncom IID,返回GUID,传递给ctypes
return UUID(str(guid))。bytes_le

proto_icf2_base = WINFUNCTYPE(HRESULT,
c_ulong,
c_ulong,
c_char_p,
c_ulong,
POINTER(c_ulong),

IClassFactory2__CreateInstanceLic = proto_icf2_base(7,'CreateInstanceLic',(
(1,'pUnkOuter'),
(1 |) 4,'pUnkReserved'),
(1,'riid'),
(1,'bstrKey'),
(2,'ppvObj'),
),_raw_guid (IID_IClassFactory2))

#--------------------------------
# --------------------------------

def _pc_wrap(iptr,resultCLSID = None):
#return win32com.client .__ WrapDispatch(iptr)
log.debug(_ pc_wrap:%s,%s%(iptr,resultCLSID))
disp = win32com.client.Dispatch iptr,resultCLSID = resultCLSID)
log.debug(_ pc_wrap:%s(%s),disp .__ class __.__ name__,disp)
return disp

def CoCreateInstanceFromFactory给出一个factory_ptr,其接口是IClassFactory,创建具有指定接口的clsid_class的实例
ClassFactory = pythoncom.ObjectFromAddress() $ factory_ptr.value,pythoncom.IID_IClassFactory)
i = ClassFactory.CreateInstance(pUnkOuter,iid_interface)
return i

def CoCreateInstanceFromFactoryLicenced(factory_ptr,key,iid_interface = pythoncom.IID_IDispatch,pUnkOuter = )
给定一个factory_ptr,其接口为IClassFactory2,使用指定的接口创建clsid_class的实例
requested_iid = _raw_guid(iid_interface)

ole_aut = WinDLL(OleAut32.dll)
key_bstr = ole_aut.SysAllocString(unicode(key))
try:
obj = IClassFactory2__CreateInstanceLic(factory_ptr,pUnkOuter或0,c_char_p(requested_iid),key_bstr)
disp_obj = pythoncom.ObjectFromAddress(obj,iid_interface)
return disp_obj
finally:
if key_bstr:
ole_aut.SysFreeString(key_bstr)
$ b b#----------------------------------

def CoReleaseObject(obj_ptr):
在COM对象上调用Release()。 obj_ptr应该是一个c_void_p
如果不是obj_ptr:
return
IUnknown__Release = WINFUNCTYPE(HRESULT)(2,'Release',(),pythoncom.IID_IUnknown)
IUnknown__Release (obj_ptr)

#-----------------------------------

def CoCreateInstanceLicenced(clsid_class,key,pythoncom_iid_interface = pythoncom.IID_IDispatch,dwClsContext = pythoncom.CLSCTX_SERVER,pythoncom_wrapdisp = True,wrapas = None):
使用IClassFactory2 :: CreateInstanceLic创建一个COM对象给出了许可证密钥。
IID_IClassFactory2 ={B196B28F-BAB4-101A-B69C-00AA00341D07}
ole = OleDLL(Ole32.dll)
clsid_class_raw = _raw_guid(clsid_class )
iclassfactory2 = _raw_guid(IID_IClassFactory2)
com_classfactory = c_void_p(0)

ole.CoGetClassObject(clsid_class_raw,dwClsContext,None,iclassfactory2,byref(com_classfactory))
try:
iptr = CoCreateInstanceFromFactoryLicenced(
factory_ptr = com_classfactory,
key = key,
iid_interface = pythoncom_iid_interface,
pUnkOuter = None,

如果pythoncom_wrapdisp:
return _pc_wrap(iptr,resultCLSID = wrapas或clsid_class)
return iptr
finally:
如果com_classfactory:
CoReleaseObject(com_classfactory)

#--------------------------------------------- --------------
#DLLs

def CoDllGetClassObject(dll_filename,clsid_class,iid_factory = pythoncom.IID_IClassFactory):
给定一个DLL文件名和一个期望的类,返回该类的工厂(作为c_void_p)
dll = OleDLL(dll_filename)
clsid_class = _raw_guid(clsid_class)
iclassfactory = _raw_guid iid_factory)
com_classfactory = c_void_p(0)
dll.DllGetClassObject(clsid_class,iclassfactory,byref(com_classfactory))
return com_classfactory

def CoCreateInstanceFromDll(dll,clsid_class, iid_interface = pythoncom.IID_IDispatch,pythoncom_wrapdisp = True,wrapas = None):
iclassfactory_ptr = CoDllGetClassObject(dll,clsid_class)
try:
iptr = CoCreateInstanceFromFactory(iclassfactory_ptr,iid_interface)
if pythoncom_wrapdisp:
return _pc_wrap(iptr,resultCLSID = wrapas或clsid_class)
return iptr
finally:
CoReleaseObject(iclassfactory_ptr)

def CoCreateInstanceFromDllLicenced(dll, clsid_class,key,iid_interface = pythoncom.IID_IDispatch,pythoncom_wrapdisp = True,wrapas = None):
iclassfactory2_ptr = CoDllGetClassObject(dll,clsid_class,iid_factory = IID_IClassFactory2)
try:
iptr = CoCreateInstanceFromFactoryLicenced(iclassfactory2_ptr ,key,iid_interface)
如果pythoncom_wrapdisp:
return _pc_wrap(iptr,resultCLSID = wrapas或clsid_class)
return iptr
finally:
CoReleaseObject(iclassfactory2_ptr)


I have three pieces of code that i'm working with at the moment:

  • A closed source application (Main.exe)
  • A closed source VB COM object implemented as a dll (comobj.dll)
  • Code that I am developing in Python

comobj.dll hosts a COM object (lets say, 'MainInteract') that I would like to use from Python. I can already use this object perfectly fine from IronPython, but due to other requirements I need to use it from regular Python. I believe the best method here is to use win32com, but I can't quite make any headway at all.

First, some working IronPython code:

import clr
import os
import sys

__dir__ = os.path.dirname(os.path.realpath(__file__))
sys.path.insert(0, __dir__)
sys.path.append(r"C:\Path\To\comobj.dll") #This is where the com object dll actually is

clr.AddReferenceToFileAndPath(os.path.join(__dir__, r'comobj_1_1.dll')) #This is the .NET interop assembly that was created automatically via SharpDevelop's COM Inspector

from comobj_1_1 import clsMainInteract

o = clsMainInteract()
o.DoStuff(True)


And now the code that I attempted in regular Python:

>>> import win32com.client
>>> win32com.client.Dispatch("{11111111-comobj_guid_i_got_from_com_inspector}")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Python26\lib\site-packages\win32com\client\__init__.py", line 95, in Dispatch
    dispatch, userName = dynamic._GetGoodDispatchAndUserName(dispatch,userName,clsctx)
  File "C:\Python26\lib\site-packages\win32com\client\dynamic.py", line 104, in _GetGoodDispatchAndUserName
    return (_GetGoodDispatch(IDispatch, clsctx), userName)
  File "C:\Python26\lib\site-packages\win32com\client\dynamic.py", line 84, in _GetGoodDispatch
    IDispatch = pythoncom.CoCreateInstance(IDispatch, None, clsctx, pythoncom.IID_IDispatch)
 pywintypes.com_error: (-2147221164, 'Class not registered', None, None)

I have also attempted using the friendly name of the TLB:

>>> import win32com.client
>>> win32com.client.Dispatch("Friendly TLB Name I Saw")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Python26\lib\site-packages\win32com\client\__init__.py", line 95, in Dispatch
dispatch, userName = dynamic._GetGoodDispatchAndUserName(dispatch,userName,clsctx)
  File "C:\Python26\lib\site-packages\win32com\client\dynamic.py", line 104, in _GetGoodDispatchAndUserName
return (_GetGoodDispatch(IDispatch, clsctx), userName)
  File "C:\Python26\lib\site-packages\win32com\client\dynamic.py", line 84, in _GetGoodDispatch
    IDispatch = pythoncom.CoCreateInstance(IDispatch, None, clsctx, pythoncom.IID_IDispatch)
pywintypes.com_error: (-2147221005, 'Invalid class string', None, None)

In fact, the only success I've had was this:

import pythoncom
tlb = pythoncom.LoadRegTypeLib("{11111111-comobj_guid_i_got_from_com_inspector}",1,1,0)
>>> tlb
<PyITypeLib at 0x00AD7D78 with obj at 0x0025EDF0>
>>> tlb.GetDocumentation(1)
(u'clsMainInteract', None, 0, None)

But i'm not sure how to go from there to getting an object. I think my problem is that I need to load the dll into my process and get it to register itself with my process's COM source, so I can properly CoCreateInstance / win32com.client.Dispatch() on it.

I have also seen Activation Contexts referenced, especially when talking about 'no registration COM', but typically in a sentences like "Windows will create a context for you if you specify the right stuff in your .manifest files". I'd like to avoid manifest files if possible, as one would be required in the same folder as the (closed source) COM object dll, and i'd rather not drop any files in that directory if I can avoid it.

Thanks for the help.

解决方案

For a useful utility module that wraps the object-from-DLL case, as well as others, see https://gist.github.com/4219140

__all__ = (
    ####### Class Objects

    #CoGetClassObject - Normal, not wrapped
    'CoDllGetClassObject', #Get ClassObject from a DLL file

    ####### ClassFactory::CreateInstance Wrappers

    'CoCreateInstanceFromFactory', #Create an object via IClassFactory::CreateInstance
    'CoCreateInstanceFromFactoryLicenced', #Create a licenced object via IClassFactory2::CreateInstanceLic

    ###### Util

    'CoReleaseObject', #Calls Release() on a COM object

    ###### Main Utility Methods

    #'CoCreateInstance', #Not wrapped, normal call
    'CoCreateInstanceLicenced', #CoCreateInstance, but with a licence key

    ###### Hacky DLL methods for reg-free COM without Activation Contexts, manifests, etc
    'CoCreateInstanceFromDll', #Given a dll, a clsid, and an iid, create an object
    'CoCreateInstanceFromDllLicenced', #Given a dll, a clsid, an iid, and a license key, create an object
)

IID_IClassFactory2 = "{B196B28F-BAB4-101A-B69C-00AA00341D07}"

from uuid import UUID
from ctypes import OleDLL, WinDLL, c_ulong, byref, WINFUNCTYPE, POINTER, c_char_p, c_void_p
from ctypes.wintypes import HRESULT
import pythoncom
import win32com.client

import logging
log = logging.getLogger(__name__)


def _raw_guid(guid):
    """Given a string GUID, or a pythoncom IID, return the GUID laid out in memory suitable for passing to ctypes"""
    return UUID(str(guid)).bytes_le

proto_icf2_base = WINFUNCTYPE(HRESULT,
    c_ulong,
    c_ulong,
    c_char_p,
    c_ulong,
    POINTER(c_ulong),
)
IClassFactory2__CreateInstanceLic = proto_icf2_base(7, 'CreateInstanceLic', (
    (1, 'pUnkOuter'),
    (1 | 4, 'pUnkReserved'),
    (1, 'riid'),
    (1, 'bstrKey'),
    (2, 'ppvObj'),
    ), _raw_guid(IID_IClassFactory2))

#--------------------------------
#--------------------------------

def _pc_wrap(iptr, resultCLSID=None):
    #return win32com.client.__WrapDispatch(iptr)
    log.debug("_pc_wrap: %s, %s"%(iptr, resultCLSID))
    disp = win32com.client.Dispatch(iptr, resultCLSID=resultCLSID)
    log.debug("_pc_wrap: %s (%s)", disp.__class__.__name__, disp)
    return disp

def CoCreateInstanceFromFactory(factory_ptr, iid_interface=pythoncom.IID_IDispatch, pUnkOuter=None):
    """Given a factory_ptr whose interface is IClassFactory, create the instance of clsid_class with the specified interface"""
    ClassFactory = pythoncom.ObjectFromAddress(factory_ptr.value, pythoncom.IID_IClassFactory)
    i = ClassFactory.CreateInstance(pUnkOuter, iid_interface)
    return i

def CoCreateInstanceFromFactoryLicenced(factory_ptr, key, iid_interface=pythoncom.IID_IDispatch, pUnkOuter=None):
    """Given a factory_ptr whose interface is IClassFactory2, create the instance of clsid_class with the specified interface"""
    requested_iid = _raw_guid(iid_interface)

    ole_aut = WinDLL("OleAut32.dll")
    key_bstr = ole_aut.SysAllocString(unicode(key))
    try:
        obj = IClassFactory2__CreateInstanceLic(factory_ptr, pUnkOuter or 0, c_char_p(requested_iid), key_bstr)
        disp_obj = pythoncom.ObjectFromAddress(obj, iid_interface)
        return disp_obj
    finally:
        if key_bstr:
            ole_aut.SysFreeString(key_bstr)

#----------------------------------

def CoReleaseObject(obj_ptr):
    """Calls Release() on a COM object. obj_ptr should be a c_void_p"""
    if not obj_ptr:
        return
    IUnknown__Release = WINFUNCTYPE(HRESULT)(2, 'Release', (), pythoncom.IID_IUnknown)
    IUnknown__Release(obj_ptr)

#-----------------------------------

def CoCreateInstanceLicenced(clsid_class, key, pythoncom_iid_interface=pythoncom.IID_IDispatch, dwClsContext=pythoncom.CLSCTX_SERVER, pythoncom_wrapdisp=True, wrapas=None):
    """Uses IClassFactory2::CreateInstanceLic to create a COM object given a licence key."""
    IID_IClassFactory2 = "{B196B28F-BAB4-101A-B69C-00AA00341D07}"
    ole = OleDLL("Ole32.dll")
    clsid_class_raw = _raw_guid(clsid_class)
    iclassfactory2 = _raw_guid(IID_IClassFactory2)
    com_classfactory = c_void_p(0)

    ole.CoGetClassObject(clsid_class_raw, dwClsContext, None, iclassfactory2, byref(com_classfactory))
    try:
        iptr = CoCreateInstanceFromFactoryLicenced(
                factory_ptr = com_classfactory,
                key=key,
                iid_interface=pythoncom_iid_interface,
                pUnkOuter=None,
        )
        if pythoncom_wrapdisp:
            return _pc_wrap(iptr, resultCLSID=wrapas or clsid_class)
        return iptr
    finally:
        if com_classfactory:
            CoReleaseObject(com_classfactory)

#-----------------------------------------------------------
#DLLs

def CoDllGetClassObject(dll_filename, clsid_class, iid_factory=pythoncom.IID_IClassFactory):
    """Given a DLL filename and a desired class, return the factory for that class (as a c_void_p)"""
    dll = OleDLL(dll_filename)
    clsid_class = _raw_guid(clsid_class)
    iclassfactory = _raw_guid(iid_factory)
    com_classfactory = c_void_p(0)
    dll.DllGetClassObject(clsid_class, iclassfactory, byref(com_classfactory))
    return com_classfactory

def CoCreateInstanceFromDll(dll, clsid_class, iid_interface=pythoncom.IID_IDispatch, pythoncom_wrapdisp=True, wrapas=None):
    iclassfactory_ptr = CoDllGetClassObject(dll, clsid_class)
    try:
        iptr = CoCreateInstanceFromFactory(iclassfactory_ptr, iid_interface)
        if pythoncom_wrapdisp:
            return _pc_wrap(iptr, resultCLSID=wrapas or clsid_class)
        return iptr
    finally:
        CoReleaseObject(iclassfactory_ptr)

def CoCreateInstanceFromDllLicenced(dll, clsid_class, key, iid_interface=pythoncom.IID_IDispatch, pythoncom_wrapdisp=True, wrapas=None):
    iclassfactory2_ptr = CoDllGetClassObject(dll, clsid_class, iid_factory=IID_IClassFactory2)
    try:
        iptr = CoCreateInstanceFromFactoryLicenced(iclassfactory2_ptr, key, iid_interface)
        if pythoncom_wrapdisp:
            return _pc_wrap(iptr, resultCLSID=wrapas or clsid_class)
        return iptr
    finally:
        CoReleaseObject(iclassfactory2_ptr)

这篇关于通过注册的TLB从python访问未注册的COM对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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