在 Python 2.x 时代貌似有支持 COM的。 http://svn.python.org/projects/ctypes/tags/release_0_6_2/ctypes/win32/com/__init__.py 这个 win32 的 COM 包到了 3.x 时代就不见了。从那里参考和借鉴了很多,也被误导了很多,因为从
2.x 到 3.x 变化很大,而且那个包里面也有很多地方写得不够好。
闲话少说,直接贴代码。没加注释,也省略一些复杂 Interface 的定义。
import atexit
import ctypes
import ctypes.wintypes
import traceback
import uuid
HRESULT= ctypes.wintypes.DWORD
LCID= ctypes.wintypes.DWORD
DISPID= ctypes.wintypes.INT
SCODE= ctypes.wintypes.DWORD
VARTYPE= ctypes.c_ushort
S_OK= 0
def __init__():
ctypes.oledll.ole32.CoInitialize(None)
atexit.register(ctypes.oledll.ole32.CoUninitialize)
__init__()
class GUID(ctypes.Structure):
_fields_ = [("Data1", ctypes.wintypes.DWORD),
("Data2", ctypes.wintypes.WORD),
("Data3", ctypes.wintypes.WORD),
("Data4", ctypes.wintypes.BYTE*8)]
def __init__(self, name=None):
iid = uuid.UUID(name)
self.Data1 = iid.time_low
self.Data2 = iid.time_mid
self.Data3 = iid.time_hi_version
data = iid.bytes
for i in range(8):
self.Data4[i] = ctypes.wintypes.BYTE(data[8+i])
def __str__(self):
s = (ctypes.c_wchar*39)()
ctypes.oledll.ole32.StringFromGUID2(ctypes.byref(self), s, 39)
return s.value
REFGUID = REFIID = RIID = ctypes.POINTER(GUID)
class _InterfaceMetaclass(type(ctypes.Structure)):
def __new__(cls, name, bases, kwds):
if "_methods_" in kwds and "_methods_" in bases[0].__dict__:
kwds["_methods_"] = bases[0]._methods_+kwds["_methods_"]
lpVTable = ctypes.POINTER(name+"_VTable")
kwds["_fields_"] = [("lpVtbl", lpVTable)]
newcls = super().__new__(cls, name, bases, kwds)
newcls.lpVTable = lpVTable
if "_methods_" in kwds:
"""make methods"""
newcls._com_methods_ = {}
i = 0
for func, proto in kwds["_methods_"]:
newcls._com_methods_[func] = proto(i, func)
setattr(newcls, func, newcls._InvokeComMethod)
i += 1
return newcls
class IUnknown(ctypes.Structure, metaclass = _InterfaceMetaclass):
def _InvokeComMethod(self, *args):
func = traceback.extract_stack()[-2][-1].split('.')[1].split('(')[0]
return self._com_methods_[func](ctypes.c_void_p(ctypes.addressof(self)), *args)
_iid_ = GUID("{618736E0-3C3D-11CF-810C-00AA00389B71}")
PIUnknown = ctypes.POINTER(IUnknown)
def from_param(self, obj):
if (type(obj) == type(ctypes.byref(ctypes.c_int())) and issubclass(obj._obj._type_, IUnknown)) \
or (isinstance(obj, ctypes._Pointer) and issubclass(obj._type_._type, IUnknown)):
return obj
raise TypeError("expected a reference to IUnknown instead of "+str(type(obj)))
ctypes.POINTER(PIUnknown).from_param = classmethod(from_param)
def STDMETHOD(restype, name, *argtypes, **kw):
return name, ctypes.WINFUNCTYPE(restype, *argtypes)
IUnknown._methods_ = [STDMETHOD(HRESULT, "QueryInterface", REFIID, ctypes.POINTER(PIUnknown)),
STDMETHOD(ctypes.wintypes.ULONG, "AddRef"),
STDMETHOD(ctypes.wintypes.ULONG, "Release")]
IUnknown = _InterfaceMetaclass("IUnknown", (ctypes.Structure,), {"_methods_": IUnknown._methods_, "_iid_": IUnknown._iid_, "_InvokeComMethod": IUnknown._InvokeComMethod})
PIUnknown = ctypes.POINTER(IUnknown)
class ITypeInfo(IUnknown, metaclass = _InterfaceMetaclass):
_iid_ = GUID("{00020401-0000-0000-C000-000000000046}")
class IDispatch(IUnknown, metaclass = _InterfaceMetaclass):
_iid_ = GUID("{00020400-0000-0000-C000-000000000046}")
class BSTR(ctypes.c_wchar_p):
def __del__(self):
ctypes.oledll.oleaut32.SysFreeString(self)
def __repr__(self):
return self.value
"""http://msdn.microsoft.com/en-us/library/windows/desktop/dd373687(v=vs.85).aspx"""
class VARIANT(ctypes.Structure):
class _U(ctypes.Union):
_fields_ = [("lVal", ctypes.wintypes.LONG),# VT_I4
("pdispVal", ctypes.POINTER(IDispatch)),# VT_IDISPATCH
("bstrVal", BSTR)]# VT_BSTR
_anonymous_ = ("_u",)
_fields_ = [("vt", VARTYPE),
("wReserved1", ctypes.wintypes.WORD),
("wReserved2", ctypes.wintypes.WORD),
("wReserved3", ctypes.wintypes.WORD),
("_u", _U)]
class DISPPARAMS(ctypes.Structure):
_fields_ = [("rgvarg", ctypes.POINTER(VARIANT)),
("rgdispidNamedArgs", ctypes.POINTER(DISPID)),
("cArgs", ctypes.wintypes.UINT),
("cNamedArgs", ctypes.wintypes.UINT)]
class EXCEPINFO(ctypes.Structure):
_fields_ = [("wCode", ctypes.wintypes.WORD),
("wReserved", ctypes.wintypes.WORD),
("bstrSource", BSTR),
("bstrDescription", BSTR),
("bstrHelpFile", BSTR),
("dwHelpContext", ctypes.wintypes.DWORD),
("pvReserved", ctypes.c_void_p),
("pfnDeferredFillIn", ctypes.c_void_p), # XXX
("scode", SCODE)]
IDispatch._methods_ = [STDMETHOD(HRESULT, "GetTypeInfoCount", ctypes.wintypes.UINT),
STDMETHOD(HRESULT, "GetTypeInfo", ctypes.wintypes.UINT, LCID, ctypes.POINTER(ctypes.POINTER(ITypeInfo))),
STDMETHOD(HRESULT, "GetIDsOfNames", REFIID, ctypes.wintypes.LPOLESTR, ctypes.wintypes.ULONG, LCID, ctypes.POINTER(DISPID)),
STDMETHOD(HRESULT, "Invoke", DISPID, REFIID, LCID, ctypes.wintypes.WORD, ctypes.POINTER(DISPPARAMS), ctypes.POINTER(VARIANT), ctypes.POINTER(EXCEPINFO), ctypes.wintypes.UINT)]
IDispatch = _InterfaceMetaclass("IDispatch", (IUnknown,), {"_methods_": IDispatch._methods_})
PIDispatch = ctypes.POINTER(IDispatch)
class IAccessible(IDispatch, metaclass = _InterfaceMetaclass):
_iid_ = GUID("{618736E0-3C3D-11CF-810C-00AA00389B71}")
_methods_ = [STDMETHOD(HRESULT, "get_accParent", ctypes.POINTER(PIDispatch)),
STDMETHOD(HRESULT, "get_accChildCount", ctypes.POINTER(ctypes.c_long)),
STDMETHOD(HRESULT, "get_accChild", VARIANT, ctypes.POINTER(PIDispatch)),
STDMETHOD(HRESULT, "get_accName", VARIANT, ctypes.POINTER(BSTR)),
STDMETHOD(HRESULT, "get_accValue", VARIANT, ctypes.POINTER(BSTR)),
STDMETHOD(HRESULT, "get_accDescription", VARIANT, ctypes.POINTER(BSTR)),
STDMETHOD(HRESULT, "get_accRole", VARIANT, ctypes.POINTER(VARIANT)),
STDMETHOD(HRESULT, "get_accState", VARIANT, ctypes.POINTER(VARIANT)),
STDMETHOD(HRESULT, "get_accHelp", VARIANT, ctypes.POINTER(BSTR)),
STDMETHOD(HRESULT, "get_accHelpTopic", ctypes.POINTER(BSTR), VARIANT, ctypes.POINTER(ctypes.c_long)),
STDMETHOD(HRESULT, "get_accKeyboardShortcut", VARIANT, ctypes.POINTER(BSTR)),
STDMETHOD(HRESULT, "get_accFocus", ctypes.POINTER(VARIANT)),
STDMETHOD(HRESULT, "get_accSelection", ctypes.POINTER(VARIANT)),
STDMETHOD(HRESULT, "get_accDefaultAction", VARIANT, ctypes.POINTER(BSTR)),
STDMETHOD(HRESULT, "accSelect", ctypes.c_long, VARIANT),
STDMETHOD(HRESULT, "accLocation", ctypes.POINTER(ctypes.c_long), ctypes.POINTER(ctypes.c_long), ctypes.POINTER(ctypes.c_long), ctypes.POINTER(ctypes.c_long), VARIANT),
STDMETHOD(HRESULT, "accNavigate", ctypes.c_long, VARIANT, ctypes.POINTER(VARIANT)),
STDMETHOD(HRESULT, "accHitTest", ctypes.c_long, ctypes.c_long, ctypes.POINTER(VARIANT)),
STDMETHOD(HRESULT, "accDoDefaultAction", VARIANT),
STDMETHOD(HRESULT, "put_accName", VARIANT, BSTR),# not supported
STDMETHOD(HRESULT, "put_accValue", VARIANT, BSTR)]
PIAccessible = ctypes.POINTER(IAccessible)
class IServiceProvider(IUnknown, metaclass = _InterfaceMetaclass):
_iid_ = GUID("{6D5140C1-7436-11CE-8034-00AA006009FA}")
_methods_ = [STDMETHOD(HRESULT, "QueryService", REFGUID, REFIID, ctypes.POINTER(PIUnknown))]
PIServiceProvider = ctypes.POINTER(IServiceProvider)