2010-06-24 156 views
11

我有代码三块,我要和目前的工作:通过注册TLB访问蟒蛇未注册的COM对象

  • 封闭源代码的应用程序(MAIN.EXE)
  • 封闭源代码我在Python正在开发的DLL(comobj.dll)实现VB COM对象
  • 代码

comobj.dll主机的COM对象(可以说,“MainInteract”),我想从使用蟒蛇。我已经可以在IronPython中完美地使用这个对象,但由于其他需求,我需要从常规的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(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) 

现在,我试图在常规的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) 

我已经使用了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) 

其实,我得到的唯一成功是这样的:

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()。

我也看到Activation Contexts被引用,特别是当谈到'没有注册COM'时,但通常在像“如果你在你的.manifest文件中指定正确的东西,Windows将为你创建一个上下文”的句子中。如果可能的话,我想避免使用清单文件,因为在(闭源代码)COM对象dll所在的文件夹中需要一个文件夹,并且如果我可以避免它,我宁愿不删除任何文件。

感谢您的帮助。

+0

我不知道答案了我的头顶,但如果你能用C++来做,那么你可以很简单地包装它。 – ConcernedOfTunbridgeWells 2010-06-24 17:46:12

+0

好的,我想这是一个选择,但我希望避免它。 – 2010-06-24 18:27:41

回答

3

为包装的对象从DLL的情况下,以及其他有用的工具模块,见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) 
8

这是我设计用来从DLL加载COM对象的方法。它基于大量关于COM的阅读等。我不是100%确定最后一行,特别是d =。我认为只有在IID_Dispatch被传入时才可以使用(你可以看到默认参数)。另外,我相信这段代码会泄漏 - 例如,DLL永远不会被卸载(使用ctypes.windll.kernel32.FreeLibraryW),并且我相信初始类工厂的COM引用计数会被关闭,因此永远不会被释放。但是,这仍然适用于我的应用程序。

import pythoncom 
import win32com.client 
def CreateInstanceFromDll(dll, clsid_class, iid_interface=pythoncom.IID_IDispatch, pUnkOuter=None, dwClsContext=pythoncom.CLSCTX_SERVER): 
    from uuid import UUID 
    from ctypes import OleDLL, c_long, byref 
    e = OleDLL(dll) 
    clsid_class = UUID(clsid_class).bytes_le 
    iclassfactory = UUID(str(pythoncom.IID_IClassFactory)).bytes_le 
    com_classfactory = c_long(0) 
    hr = e.DllGetClassObject(clsid_class, iclassfactory, byref(com_classfactory)) 
    MyFactory = pythoncom.ObjectFromAddress(com_classfactory.value, pythoncom.IID_IClassFactory) 
    i = MyFactory.CreateInstance(pUnkOuter, iid_interface) 
    d = win32com.client.__WrapDispatch(i) 
    return d 
+0

测试:此解决方案有效。不知道是否泄漏 – yanjost 2013-03-26 08:35:40

+0

您可能会对https://gist.github.com/CBWhiz/4219140感兴趣,其中CoCreateInstanceFromDll的实现方式大致相同。 – 2013-03-26 17:59:17

10

我所做的访问免费下载管理器的类型库为以下几点:

import pythoncom, win32com.client 

fdm = pythoncom.LoadTypeLib('fdm.tlb') 
downloads_stat = None 

for index in xrange(0, fdm.GetTypeInfoCount()): 
    type_name = fdm.GetDocumentation(index)[0] 

    if type_name == 'FDMDownloadsStat': 
     type_iid = fdm.GetTypeInfo(index).GetTypeAttr().iid 
     downloads_stat = win32com.client.Dispatch(type_iid) 
     break 

downloads_stat.BuildListOfDownloads(True, True) 
print downloads_stat.Download(0).Url 

上面的代码将打印第一次下载的URL。

+0

谢谢,MárcioFaustino!大!这对我来说很好! – ECC 2014-07-22 11:31:02