import struct import ctypes import functools from ctypes.wintypes import HRESULT, byref, pointer, cast import windows from windows import winproxy from windows.generated_def.winstructs import * from windows.generated_def import RPC_C_IMP_LEVEL_IMPERSONATE, CLSCTX_INPROC_SERVER from windows.generated_def import interfaces from windows.generated_def.interfaces import generate_IID, IID # Simple raw -> UUID # "-".join("{:02X}".format(c) for c in struct.unpack("``""" if custom_iid is None: custom_iid = targetinterface.IID return winproxy.CoCreateInstance(byref(clsiid), None, context, byref(custom_iid), byref(targetinterface)) # Improved COM object # Todo: ctypes_genertation extended struct ? class ImprovedSAFEARRAY(SAFEARRAY): @classmethod def of_type(cls, addr, t): self = cls.from_address(addr) self.elt_type = t return self @classmethod def from_PSAFEARRAY(self, psafearray): res = cast(psafearray, POINTER(ImprovedSAFEARRAY))[0] return res def to_list(self, t=None): if t is None: if hasattr(self, "elt_type"): t = self.elt_type else: raise ValueError("Missing type of the array") if self.cDims != 1: raise NotImplementedError("tagSAFEARRAY if dims != 1") nb_element = self.rgsabound[0].cElements llbound = self.rgsabound[0].lLbound if self.cbElements != ctypes.sizeof(t): raise ValueError("Size of elements != sizeof(type)") data = [t.from_address(self.pvData + (i + llbound) * ctypes.sizeof(t)).value for i in range(nb_element)] return data #VT_VALUE_TO_TYPE = { #VT_I2 : SHORT, #VT_I4 : LONG, #VT_BSTR : BSTR, #VT_VARIANT : VARIANT, #VT_UI1 : UCHAR, #VT_UI2 : USHORT, #VT_UI4 : DWORD, #VT_I8 : LONGLONG, #VT_UI8 : ULONG64, #VT_INT : INT, #VT_UINT : UINT, #VT_HRESULT : HRESULT, #VT_PTR : PVOID, #VT_LPSTR : LPCSTR, #VT_LPWSTR : LPWSTR, #} class ImprovedVariant(VARIANT): @property def asbstr(self): if self.vt != VT_BSTR: raise ValueError("asbstr on non-bstr variant") #import pdb;pdb.set_trace() return self._VARIANT_NAME_3.bstrVal @property def aslong(self): if not self.vt in [VT_I4]: raise ValueError("aslong on non-long variant") return self._VARIANT_NAME_3.lVal @property def asbool(self): if not self.vt in [VT_BOOL]: raise ValueError("get_bstr on non-bool variant") return bool(self._VARIANT_NAME_3.boolVal) @property def asdispatch(self): if not self.vt in [VT_DISPATCH]: raise ValueError("asdispatch on non-VT_DISPATCH variant") return interfaces.IDispatch(self._VARIANT_NAME_3.pdispVal) @property def asshort(self): if not self.vt in [VT_I2]: raise ValueError("asshort on non-VT_I2 variant") return self._VARIANT_NAME_3.iVal @property def asbyte(self): if not self.vt in [VT_UI1]: raise ValueError("asbyte on non-VT_UI1 variant") return self._VARIANT_NAME_3.bVal @property def asunknown(self): if not self.vt in [VT_UNKNOWN]: raise ValueError("asunknown on non-VT_UNKNOWN variant") return self._VARIANT_NAME_3.punkVal @property def asarray(self): if not self.vt & VT_ARRAY: raise ValueError("asarray on non-VT_ARRAY variant") # TODO: auto extract VT_TYPE for the array ? #type = VT_VALUE_TO_TYPE[self.vt & VT_TYPEMASK] return ImprovedSAFEARRAY.from_PSAFEARRAY(self._VARIANT_NAME_3.parray) @property def aslong_array(self): if not self.vt & VT_I4: raise ValueError("as_bstr_array on non-VT_BSTR variant") return self.asarray.to_list(LONG) def generate_asarray_property(vttype): @property def as_array_generated(self): # TODO: vt check like the others ? return self.asarray.to_list(vttype) return as_array_generated asbstr_array = generate_asarray_property(BSTR) aslong_array = generate_asarray_property(LONG) asbyte_array = generate_asarray_property(BYTE) asbool_array = generate_asarray_property(VARIANT_BOOL) def to_pyobject(self): # if self.vt & VT_ARRAY: # # Something better TODO i guess # if self.vt & VT_TYPEMASK == VT_BSTR: # import pdb;pdb.set_trace() # print("VT_TYPEMASK ARRAY") # return self.asarray.to_list(BSTR) # if self.vt & VT_TYPEMASK == VT_I4: # import pdb;pdb.set_trace() # print("VT_TYPEMASK ARRAY") # return self.asarray.to_list(LONG) # raise NotImplementedError("Variant of type {0:#x}".format(self.vt)) # use the ImprovedVariant.MAPPER that dispatch by self.vt try: return self.MAPPER[self.vt](self) except KeyError: raise NotImplementedError("Variant of type {0:#x}".format(self.vt)) ImprovedVariant.MAPPER = { VT_UI1: ImprovedVariant.asbyte.fget, VT_I2: ImprovedVariant.asshort.fget, VT_DISPATCH: ImprovedVariant.asdispatch.fget, VT_BOOL: ImprovedVariant.asbool.fget, VT_I4: ImprovedVariant.aslong.fget, VT_BSTR: ImprovedVariant.asbstr.fget, VT_EMPTY: (lambda x: None), VT_NULL: (lambda x: None), VT_UNKNOWN: ImprovedVariant.asunknown.fget, (VT_ARRAY | VT_BSTR): ImprovedVariant.asbstr_array.fget, (VT_ARRAY | VT_I4): ImprovedVariant.aslong_array.fget, (VT_ARRAY | VT_UI1): ImprovedVariant.asbyte_array.fget, (VT_ARRAY | VT_BOOL): ImprovedVariant.asbool_array.fget } class COMImplementation(object): """The base class to implements COM object respecting a given interface""" IMPLEMENT = None def get_index_of_method(self, method): # This code is horrible but not totally my fault # the PyCFuncPtrObject->index is not exposed to Python.. # repr is: '' rpr = repr(method) if not rpr.startswith(" implementing <{1}> has no method <{2}>".format(type(self).__name__, self.IMPLEMENT.__name__, func_name)) if not callable(implem): raise ValueError("{0} implementing <{1}>: <{2}> is not callable".format(type(self).__name__, self.IMPLEMENT.__name__, func_name)) return True def _create_vtable(self, interface): implems = [] names = [] for index, name, method in self.extract_methods_order(interface): func_implem = getattr(self, name) #'this' is a COM-interface of the type we are implementing types = [method.restype, interface] + list(method.argtypes) implems.append(ctypes.WINFUNCTYPE(*types)(func_implem)) names.append(name) class Vtable(ctypes.Structure): _fields_ = [(name, ctypes.c_void_p) for name in names] return Vtable(*[ctypes.cast(x, ctypes.c_void_p) for x in implems]), implems def __init__(self): self.verify_implem(self.IMPLEMENT) vtable, implems = self._create_vtable(self.IMPLEMENT) self.vtable = vtable self.implems = implems self.vtable_pointer = ctypes.pointer(self.vtable) self._as_parameter_ = ctypes.addressof(self.vtable_pointer) def QueryInterface(self, this, piid, result): """Default ``QueryInterface`` implementation that returns ``self`` if piid is the implemented interface""" if piid[0] in (IUnknown.IID, self.IMPLEMENT.IID): result[0] = this return 1 return E_NOINTERFACE def AddRef(self, *args): """Default ``AddRef`` implementation that returns ``1``""" return 1 def Release(self, *args): """Default ``Release`` implementation that returns ``1``""" return 0