如何从 python 中的 dll 加载带有自定义接口的 COM 对象?
How to load COM object with custom interface from dll in python?
大家好。
任务是使用从 dll 加载的 COM 对象(禁止使用 regsvr32)。该对象还公开了我将来需要的 DirectShow 接口。
然后我尝试使用 this link, I'm running into a problem: pythoncom doesn't know a thing about DirectShow interfaces (IBaseFilter for instance). And from this post 中的示例获取模块 我的印象是 pythoncom 不支持自定义 COM 接口,但那是在 2008 年,也许现在情况发生了变化?
密码是
# -*- coding: utf-8 -*-
import ctypes, inspect, os, pythoncom, sys
from comtypes import client
from ctypes import OleDLL, c_long, byref
from uuid import UUID
#dshow is a module with DirectShow constants, etc
cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],"path_to_dshow_module")))
if cmd_subfolder not in sys.path:
sys.path.insert(0, cmd_subfolder)
import dshow
#that way comtypes gets to know about DirectShow interfaces
qedit = client.GetModule("qedit.dll")
dll_path = os.path.join(cmd_subfolder, "../my_path/my_dshow_filter.ax") #specifying path to dll
iid_interface = dshow.CLSID_IUnknown
iid_ibasefilter = dshow.CLSID_IBaseFilter
clsid_class = UUID(dshow.CLSID_my_filter).bytes_le
iclassfactory = UUID(str(pythoncom.IID_IClassFactory)).bytes_le
com_classfactory = c_long(0)
my_dll = ctypes.oledll.LoadLibrary(dll_path)
#getting com_classfactory pointer to an adress of IClassFactory within loaded dll
hr = my_dll.DllGetClassObject(clsid_class, iclassfactory, byref(com_classfactory))
#creating class factory from adress using pythoncom
MyFactory = pythoncom.ObjectFromAddress(com_classfactory.value, pythoncom.IID_IClassFactory)
#creating COM object using IClassFactory::CreateInstance, using IUnknown as a default interface
dmx_interface = MyFactory.CreateInstance(None, iid_interface)
# I could've tried to use IBaseFilter directly,
# but pythoncom knows nothing about DirectShow interfaces!
# dmx = dmx_interface.QueryInterface(str(qedit.IBaseFilter._iid_)) #that yields an error
dmx = dmx_interface.QueryInterface(iid_ibasefilter) #that yields the same error
我得到的错误是TypeError: There is no interface object registered that supports this IID
,这是可以理解的。
所以,comtypes 知道那个接口!但不幸的是,我找不到使用 comtypes 甚至 ctypes.
从 dll 加载 COM 对象的方法
我已经处理这个问题好几天了,非常感谢您的建议。
最后,它需要一些指针操作,但我做到了。
我从 comtypes.server 导入了 class IClassFactory 并为它获取了一个指针(指针 № 1).之后,我在加载的 dll 中获得了指向 IClassFactory 对象的 c_long 指针(指针 №2)。最后,我将指针 №2 的值赋给了指针 №1。
from comtypes.server import IClassFactory
#same code as it was before
dll_path = os.path.join(cmd_subfolder, "../my_path/my_dshow_filter.ax")
clsid_class = UUID(dshow.CLSID_my_filter).bytes_le
#that may be replaced with other IID_IClassFactory definition
#so no pythoncom is required at all
iclassfactory = UUID(str(pythoncom.IID_IClassFactory)).bytes_le
com_classfactory = c_long(0)
my_dll = ctypes.oledll.LoadLibrary(dll)
hr = my_dll.DllGetClassObject(clsid_class, iclassfactory, byref(com_classfactory))
ptr_icf = POINTER(IClassFactory)(com_classfactory.value) #pointer to IClassFactory
#and there we'll have a pointer to IUknown of the filter inside the dll
filter = ptr_icf.CreateInstance()
dec = filter.QueryInterface(qedit.IBaseFilter)
filter_graph.AddFilter(dec, "dec")
#Voila!
所以,这项工作可以在不使用 pythoncom 的情况下完成,这对我来说是一个很大的优势(因为之前的所有工作都是通过 comtypes 模块)
大家好。 任务是使用从 dll 加载的 COM 对象(禁止使用 regsvr32)。该对象还公开了我将来需要的 DirectShow 接口。
然后我尝试使用 this link, I'm running into a problem: pythoncom doesn't know a thing about DirectShow interfaces (IBaseFilter for instance). And from this post 中的示例获取模块 我的印象是 pythoncom 不支持自定义 COM 接口,但那是在 2008 年,也许现在情况发生了变化?
密码是
# -*- coding: utf-8 -*-
import ctypes, inspect, os, pythoncom, sys
from comtypes import client
from ctypes import OleDLL, c_long, byref
from uuid import UUID
#dshow is a module with DirectShow constants, etc
cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],"path_to_dshow_module")))
if cmd_subfolder not in sys.path:
sys.path.insert(0, cmd_subfolder)
import dshow
#that way comtypes gets to know about DirectShow interfaces
qedit = client.GetModule("qedit.dll")
dll_path = os.path.join(cmd_subfolder, "../my_path/my_dshow_filter.ax") #specifying path to dll
iid_interface = dshow.CLSID_IUnknown
iid_ibasefilter = dshow.CLSID_IBaseFilter
clsid_class = UUID(dshow.CLSID_my_filter).bytes_le
iclassfactory = UUID(str(pythoncom.IID_IClassFactory)).bytes_le
com_classfactory = c_long(0)
my_dll = ctypes.oledll.LoadLibrary(dll_path)
#getting com_classfactory pointer to an adress of IClassFactory within loaded dll
hr = my_dll.DllGetClassObject(clsid_class, iclassfactory, byref(com_classfactory))
#creating class factory from adress using pythoncom
MyFactory = pythoncom.ObjectFromAddress(com_classfactory.value, pythoncom.IID_IClassFactory)
#creating COM object using IClassFactory::CreateInstance, using IUnknown as a default interface
dmx_interface = MyFactory.CreateInstance(None, iid_interface)
# I could've tried to use IBaseFilter directly,
# but pythoncom knows nothing about DirectShow interfaces!
# dmx = dmx_interface.QueryInterface(str(qedit.IBaseFilter._iid_)) #that yields an error
dmx = dmx_interface.QueryInterface(iid_ibasefilter) #that yields the same error
我得到的错误是TypeError: There is no interface object registered that supports this IID
,这是可以理解的。
所以,comtypes 知道那个接口!但不幸的是,我找不到使用 comtypes 甚至 ctypes.
从 dll 加载 COM 对象的方法我已经处理这个问题好几天了,非常感谢您的建议。
最后,它需要一些指针操作,但我做到了。
我从 comtypes.server 导入了 class IClassFactory 并为它获取了一个指针(指针 № 1).之后,我在加载的 dll 中获得了指向 IClassFactory 对象的 c_long 指针(指针 №2)。最后,我将指针 №2 的值赋给了指针 №1。
from comtypes.server import IClassFactory
#same code as it was before
dll_path = os.path.join(cmd_subfolder, "../my_path/my_dshow_filter.ax")
clsid_class = UUID(dshow.CLSID_my_filter).bytes_le
#that may be replaced with other IID_IClassFactory definition
#so no pythoncom is required at all
iclassfactory = UUID(str(pythoncom.IID_IClassFactory)).bytes_le
com_classfactory = c_long(0)
my_dll = ctypes.oledll.LoadLibrary(dll)
hr = my_dll.DllGetClassObject(clsid_class, iclassfactory, byref(com_classfactory))
ptr_icf = POINTER(IClassFactory)(com_classfactory.value) #pointer to IClassFactory
#and there we'll have a pointer to IUknown of the filter inside the dll
filter = ptr_icf.CreateInstance()
dec = filter.QueryInterface(qedit.IBaseFilter)
filter_graph.AddFilter(dec, "dec")
#Voila!
所以,这项工作可以在不使用 pythoncom 的情况下完成,这对我来说是一个很大的优势(因为之前的所有工作都是通过 comtypes 模块)