سؤال

لدي ثلاث قطع من التعليمات البرمجية التي أعمل معها في الوقت الحالي:

  • تطبيق مصدر مغلق (main.exe)
  • تم تنفيذ كائن مصدر مغلق VB COM كـ DLL (comobj.dll)
  • الكود الذي أقوم بتطويره في بيثون

يستضيف comobj.dll كائن COM (دعنا نقول ، "MainInteract") الذي أود استخدامه من Python. يمكنني بالفعل استخدام هذا الكائن جيدًا تمامًا من 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)

والآن الكود الذي حاولت في بيثون العادية:

>>> 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 () عليه.

لقد رأيت أيضا سياقات التنشيط تمت الإشارة إليها ، خاصة عند الحديث عن "لا يوجد تسجيل كوم" ، ولكن عادةً في جمل مثل "Windows سيقوم بإنشاء سياق لك إذا قمت بتحديد الأشياء المناسبة في ملفاتك. أرغب في تجنب الملفات الواضحة إن أمكن ، كما هو مطلوب في نفس المجلد مثل (المصدر المغلق) كائن com DLL ، وأفضل عدم إسقاط أي ملفات في هذا الدليل إذا كان بإمكاني تجنب ذلك.

شكرا للمساعدة.

هل كانت مفيدة؟

المحلول 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)

نصائح أخرى

ما فعلته للوصول إلى مكتبة نوع التنزيل المجاني لمدير من نوع ما يلي:

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 للتنزيل الأول.

فيما يلي طريقة ابتكرت لتحميل كائن COM من DLL. كان يعتمد على الكثير من القراءة حول COM ، وما إلى ذلك. لست متأكدًا بنسبة 100 ٪ من الأسطر الأخيرة ، وتحديداً D =. أعتقد أن هذا يعمل فقط إذا تم تمرير IID_Dispatch (يمكنك معرفة ما إذا كان المعلمة الافتراضية).

بالإضافة إلى ذلك ، أعتقد أن هذا الكود يتسرب - على سبيل المثال ، لا يتم تفريغ DLL أبدًا (استخدم ctypes.windll.kernel32.freelibraryw) وأعتقد أن تعداد Com Ref لمصنع الفئة الأولي يتم إيقاف تشغيله من قبل واحد ، وبالتالي لا يتم إصداره أبدًا. ولكن لا يزال هذا يناسب طلبي.

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
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top