# comtypes.automation module import array import datetime import decimal from ctypes import * from ctypes import _Pointer from _ctypes import CopyComPointer from comtypes import IUnknown, GUID, IID, STDMETHOD, BSTR, COMMETHOD, COMError from comtypes.hresult import * from comtypes.patcher import Patch from comtypes import npsupport try: from comtypes import _safearray except (ImportError, AttributeError): class _safearray(object): tagSAFEARRAY = None from ctypes.wintypes import DWORD, LONG, UINT, VARIANT_BOOL, WCHAR, WORD LCID = DWORD DISPID = LONG SCODE = LONG VARTYPE = c_ushort DISPATCH_METHOD = 1 DISPATCH_PROPERTYGET = 2 DISPATCH_PROPERTYPUT = 4 DISPATCH_PROPERTYPUTREF = 8 tagINVOKEKIND = c_int INVOKE_FUNC = DISPATCH_METHOD INVOKE_PROPERTYGET = DISPATCH_PROPERTYGET INVOKE_PROPERTYPUT = DISPATCH_PROPERTYPUT INVOKE_PROPERTYPUTREF = DISPATCH_PROPERTYPUTREF INVOKEKIND = tagINVOKEKIND ################################ # helpers IID_NULL = GUID() riid_null = byref(IID_NULL) _byref_type = type(byref(c_int())) # 30. December 1899, midnight. For VT_DATE. _com_null_date = datetime.datetime(1899, 12, 30, 0, 0, 0) ################################################################ # VARIANT, in all it's glory. VARENUM = c_int # enum VT_EMPTY = 0 VT_NULL = 1 VT_I2 = 2 VT_I4 = 3 VT_R4 = 4 VT_R8 = 5 VT_CY = 6 VT_DATE = 7 VT_BSTR = 8 VT_DISPATCH = 9 VT_ERROR = 10 VT_BOOL = 11 VT_VARIANT = 12 VT_UNKNOWN = 13 VT_DECIMAL = 14 VT_I1 = 16 VT_UI1 = 17 VT_UI2 = 18 VT_UI4 = 19 VT_I8 = 20 VT_UI8 = 21 VT_INT = 22 VT_UINT = 23 VT_VOID = 24 VT_HRESULT = 25 VT_PTR = 26 VT_SAFEARRAY = 27 VT_CARRAY = 28 VT_USERDEFINED = 29 VT_LPSTR = 30 VT_LPWSTR = 31 VT_RECORD = 36 VT_INT_PTR = 37 VT_UINT_PTR = 38 VT_FILETIME = 64 VT_BLOB = 65 VT_STREAM = 66 VT_STORAGE = 67 VT_STREAMED_OBJECT = 68 VT_STORED_OBJECT = 69 VT_BLOB_OBJECT = 70 VT_CF = 71 VT_CLSID = 72 VT_VERSIONED_STREAM = 73 VT_BSTR_BLOB = 4095 VT_VECTOR = 4096 VT_ARRAY = 8192 VT_BYREF = 16384 VT_RESERVED = 32768 VT_ILLEGAL = 65535 VT_ILLEGALMASKED = 4095 VT_TYPEMASK = 4095 class tagCY(Structure): _fields_ = [("int64", c_longlong)] CY = tagCY CURRENCY = CY class tagDEC(Structure): _fields_ = [("wReserved", c_ushort), ("scale", c_ubyte), ("sign", c_ubyte), ("Hi32", c_ulong), ("Lo64", c_ulonglong)] def as_decimal(self): """ Convert a tagDEC struct to Decimal. See http://msdn.microsoft.com/en-us/library/cc234586.aspx for the tagDEC specification. """ digits = (self.Hi32 << 64) + self.Lo64 decimal_str = "{0}{1}e-{2}".format( '-' if self.sign else '', digits, self.scale, ) return decimal.Decimal(decimal_str) DECIMAL = tagDEC # The VARIANT structure is a good candidate for implementation in a C # helper extension. At least the get/set methods. class tagVARIANT(Structure): class U_VARIANT1(Union): class __tagVARIANT(Structure): # The C Header file defn of VARIANT is much more complicated, but # this is the ctypes version - functional as well. class U_VARIANT2(Union): class _tagBRECORD(Structure): _fields_ = [("pvRecord", c_void_p), ("pRecInfo", POINTER(IUnknown))] _fields_ = [ ("VT_BOOL", VARIANT_BOOL), ("VT_I1", c_byte), ("VT_I2", c_short), ("VT_I4", c_long), ("VT_I8", c_longlong), ("VT_INT", c_int), ("VT_UI1", c_ubyte), ("VT_UI2", c_ushort), ("VT_UI4", c_ulong), ("VT_UI8", c_ulonglong), ("VT_UINT", c_uint), ("VT_R4", c_float), ("VT_R8", c_double), ("VT_CY", c_longlong), ("c_wchar_p", c_wchar_p), ("c_void_p", c_void_p), ("pparray", POINTER(POINTER(_safearray.tagSAFEARRAY))), ("bstrVal", BSTR), ("_tagBRECORD", _tagBRECORD), ] _anonymous_ = ["_tagBRECORD"] _fields_ = [("vt", VARTYPE), ("wReserved1", c_ushort), ("wReserved2", c_ushort), ("wReserved3", c_ushort), ("_", U_VARIANT2) ] _fields_ = [("__VARIANT_NAME_2", __tagVARIANT), ("decVal", DECIMAL)] _anonymous_ = ["__VARIANT_NAME_2"] _fields_ = [("__VARIANT_NAME_1", U_VARIANT1)] _anonymous_ = ["__VARIANT_NAME_1"] def __init__(self, *args): if args: self.value = args[0] def __del__(self): if self._b_needsfree_: # XXX This does not work. _b_needsfree_ is never # set because the buffer is internal to the object. _VariantClear(self) def __repr__(self): if self.vt & VT_BYREF: return "VARIANT(vt=0x%x, byref(%r))" % (self.vt, self[0]) return "VARIANT(vt=0x%x, %r)" % (self.vt, self.value) def from_param(cls, value): if isinstance(value, cls): return value return cls(value) from_param = classmethod(from_param) def __setitem__(self, index, value): # This method allows to change the value of a # (VT_BYREF|VT_xxx) variant in place. if index != 0: raise IndexError(index) if not self.vt & VT_BYREF: raise TypeError("set_byref requires a VT_BYREF VARIANT instance") typ = _vartype_to_ctype[self.vt & ~VT_BYREF] cast(self._.c_void_p, POINTER(typ))[0] = value # see also c:/sf/pywin32/com/win32com/src/oleargs.cpp 54 def _set_value(self, value): _VariantClear(self) if value is None: self.vt = VT_NULL elif (hasattr(value, '__len__') and len(value) == 0 and not isinstance(value, str)): self.vt = VT_NULL # since bool is a subclass of int, this check must come before # the check for int elif isinstance(value, bool): self.vt = VT_BOOL self._.VT_BOOL = value elif isinstance(value, (int, c_int)): self.vt = VT_I4 self._.VT_I4 = value elif isinstance(value, int): u = self._ # try VT_I4 first. u.VT_I4 = value if u.VT_I4 == value: # it did work. self.vt = VT_I4 return # try VT_UI4 next. if value >= 0: u.VT_UI4 = value if u.VT_UI4 == value: # did work. self.vt = VT_UI4 return # try VT_I8 next. if value >= 0: u.VT_I8 = value if u.VT_I8 == value: # did work. self.vt = VT_I8 return # try VT_UI8 next. if value >= 0: u.VT_UI8 = value if u.VT_UI8 == value: # did work. self.vt = VT_UI8 return # VT_R8 is last resort. self.vt = VT_R8 u.VT_R8 = float(value) elif isinstance(value, (float, c_double)): self.vt = VT_R8 self._.VT_R8 = value elif isinstance(value, str): self.vt = VT_BSTR # do the c_wchar_p auto unicode conversion self._.c_void_p = _SysAllocStringLen(value, len(value)) elif isinstance(value, datetime.datetime): delta = value - _com_null_date # a day has 24 * 60 * 60 = 86400 seconds com_days = delta.days + (delta.seconds + delta.microseconds * 1e-6) / 86400. self.vt = VT_DATE self._.VT_R8 = com_days elif npsupport.isdatetime64(value): com_days = value - npsupport.com_null_date64 com_days /= npsupport.numpy.timedelta64(1, 'D') self.vt = VT_DATE self._.VT_R8 = com_days elif decimal is not None and isinstance(value, decimal.Decimal): self._.VT_CY = int(round(value * 10000)) self.vt = VT_CY elif isinstance(value, POINTER(IDispatch)): CopyComPointer(value, byref(self._)) self.vt = VT_DISPATCH elif isinstance(value, POINTER(IUnknown)): CopyComPointer(value, byref(self._)) self.vt = VT_UNKNOWN elif isinstance(value, (list, tuple)): obj = _midlSAFEARRAY(VARIANT).create(value) memmove(byref(self._), byref(obj), sizeof(obj)) self.vt = VT_ARRAY | obj._vartype_ elif isinstance(value, array.array): vartype = _arraycode_to_vartype[value.typecode] typ = _vartype_to_ctype[vartype] obj = _midlSAFEARRAY(typ).create(value) memmove(byref(self._), byref(obj), sizeof(obj)) self.vt = VT_ARRAY | obj._vartype_ elif npsupport.isndarray(value): # Try to convert a simple array of basic types. descr = value.dtype.descr[0][1] typ = npsupport.numpy.ctypeslib._typecodes.get(descr) if typ is None: # Try for variant obj = _midlSAFEARRAY(VARIANT).create(value) else: obj = _midlSAFEARRAY(typ).create(value) memmove(byref(self._), byref(obj), sizeof(obj)) self.vt = VT_ARRAY | obj._vartype_ elif isinstance(value, Structure) and hasattr(value, "_recordinfo_"): guids = value._recordinfo_ from comtypes.typeinfo import GetRecordInfoFromGuids ri = GetRecordInfoFromGuids(*guids) self.vt = VT_RECORD # Assigning a COM pointer to a structure field does NOT # call AddRef(), have to call it manually: ri.AddRef() self._.pRecInfo = ri self._.pvRecord = ri.RecordCreateCopy(byref(value)) elif isinstance(getattr(value, "_comobj", None), POINTER(IDispatch)): CopyComPointer(value._comobj, byref(self._)) self.vt = VT_DISPATCH elif isinstance(value, VARIANT): _VariantCopy(self, value) elif isinstance(value, c_ubyte): self._.VT_UI1 = value self.vt = VT_UI1 elif isinstance(value, c_char): self._.VT_UI1 = ord(value.value) self.vt = VT_UI1 elif isinstance(value, c_byte): self._.VT_I1 = value self.vt = VT_I1 elif isinstance(value, c_ushort): self._.VT_UI2 = value self.vt = VT_UI2 elif isinstance(value, c_short): self._.VT_I2 = value self.vt = VT_I2 elif isinstance(value, c_uint): self.vt = VT_UI4 self._.VT_UI4 = value elif isinstance(value, c_float): self.vt = VT_R4 self._.VT_R4 = value elif isinstance(value, c_int64): self.vt = VT_I8 self._.VT_I8 = value elif isinstance(value, c_uint64): self.vt = VT_UI8 self._.VT_UI8 = value elif isinstance(value, _byref_type): ref = value._obj self._.c_void_p = addressof(ref) self.__keepref = value self.vt = _ctype_to_vartype[type(ref)] | VT_BYREF elif isinstance(value, _Pointer): ref = value.contents self._.c_void_p = addressof(ref) self.__keepref = value self.vt = _ctype_to_vartype[type(ref)] | VT_BYREF else: raise TypeError("Cannot put %r in VARIANT" % value) # buffer -> SAFEARRAY of VT_UI1 ? # c:/sf/pywin32/com/win32com/src/oleargs.cpp 197 def _get_value(self, dynamic=False): vt = self.vt if vt in (VT_EMPTY, VT_NULL): return None elif vt == VT_I1: return self._.VT_I1 elif vt == VT_I2: return self._.VT_I2 elif vt == VT_I4: return self._.VT_I4 elif vt == VT_I8: return self._.VT_I8 elif vt == VT_UI8: return self._.VT_UI8 elif vt == VT_INT: return self._.VT_INT elif vt == VT_UI1: return self._.VT_UI1 elif vt == VT_UI2: return self._.VT_UI2 elif vt == VT_UI4: return self._.VT_UI4 elif vt == VT_UINT: return self._.VT_UINT elif vt == VT_R4: return self._.VT_R4 elif vt == VT_R8: return self._.VT_R8 elif vt == VT_BOOL: return self._.VT_BOOL elif vt == VT_BSTR: return self._.bstrVal elif vt == VT_DATE: days = self._.VT_R8 return datetime.timedelta(days=days) + _com_null_date elif vt == VT_CY: return self._.VT_CY / decimal.Decimal("10000") elif vt == VT_UNKNOWN: val = self._.c_void_p if not val: # We should/could return a NULL COM pointer. # But the code generation must be able to construct one # from the __repr__ of it. return None # XXX? ptr = cast(val, POINTER(IUnknown)) # cast doesn't call AddRef (it should, imo!) ptr.AddRef() return ptr.__ctypes_from_outparam__() elif vt == VT_DECIMAL: return self.decVal.as_decimal() elif vt == VT_DISPATCH: val = self._.c_void_p if not val: # See above. return None # XXX? ptr = cast(val, POINTER(IDispatch)) # cast doesn't call AddRef (it should, imo!) ptr.AddRef() if not dynamic: return ptr.__ctypes_from_outparam__() else: from comtypes.client.dynamic import Dispatch return Dispatch(ptr) # see also c:/sf/pywin32/com/win32com/src/oleargs.cpp elif self.vt & VT_BYREF: return self elif vt == VT_RECORD: from comtypes.client import GetModule from comtypes.typeinfo import IRecordInfo # Retrieving a COM pointer from a structure field does NOT # call AddRef(), have to call it manually: punk = self._.pRecInfo punk.AddRef() ri = punk.QueryInterface(IRecordInfo) # find typelib tlib = ri.GetTypeInfo().GetContainingTypeLib()[0] # load typelib wrapper module mod = GetModule(tlib) # retrive the type and create an instance value = getattr(mod, ri.GetName())() # copy data into the instance ri.RecordCopy(self._.pvRecord, byref(value)) return value elif self.vt & VT_ARRAY: typ = _vartype_to_ctype[self.vt & ~VT_ARRAY] return cast(self._.pparray, _midlSAFEARRAY(typ)).unpack() else: raise NotImplementedError("typecode %d = 0x%x)" % (vt, vt)) def __getitem__(self, index): if index != 0: raise IndexError(index) if self.vt == VT_BYREF|VT_VARIANT: v = VARIANT() # apparently VariantCopyInd doesn't work always with # VT_BYREF|VT_VARIANT, so do it manually. v = cast(self._.c_void_p, POINTER(VARIANT))[0] return v.value else: v = VARIANT() _VariantCopyInd(v, self) return v.value # these are missing: ## getter[VT_ERROR] ## getter[VT_ARRAY] ## getter[VT_BYREF|VT_UI1] ## getter[VT_BYREF|VT_I2] ## getter[VT_BYREF|VT_I4] ## getter[VT_BYREF|VT_R4] ## getter[VT_BYREF|VT_R8] ## getter[VT_BYREF|VT_BOOL] ## getter[VT_BYREF|VT_ERROR] ## getter[VT_BYREF|VT_CY] ## getter[VT_BYREF|VT_DATE] ## getter[VT_BYREF|VT_BSTR] ## getter[VT_BYREF|VT_UNKNOWN] ## getter[VT_BYREF|VT_DISPATCH] ## getter[VT_BYREF|VT_ARRAY] ## getter[VT_BYREF|VT_VARIANT] ## getter[VT_BYREF] ## getter[VT_BYREF|VT_DECIMAL] ## getter[VT_BYREF|VT_I1] ## getter[VT_BYREF|VT_UI2] ## getter[VT_BYREF|VT_UI4] ## getter[VT_BYREF|VT_INT] ## getter[VT_BYREF|VT_UINT] value = property(_get_value, _set_value) def __ctypes_from_outparam__(self): # XXX Manual resource management, because of the VARIANT bug: result = self.value self.value = None return result def ChangeType(self, typecode): _VariantChangeType(self, self, 0, typecode) VARIANT = tagVARIANT VARIANTARG = VARIANT _oleaut32 = OleDLL("oleaut32") _VariantChangeType = _oleaut32.VariantChangeType _VariantChangeType.argtypes = (POINTER(VARIANT), POINTER(VARIANT), c_ushort, VARTYPE) _VariantClear = _oleaut32.VariantClear _VariantClear.argtypes = (POINTER(VARIANT),) _SysAllocStringLen = windll.oleaut32.SysAllocStringLen _SysAllocStringLen.argtypes = c_wchar_p, c_uint _SysAllocStringLen.restype = c_void_p _VariantCopy = _oleaut32.VariantCopy _VariantCopy.argtypes = POINTER(VARIANT), POINTER(VARIANT) _VariantCopyInd = _oleaut32.VariantCopyInd _VariantCopyInd.argtypes = POINTER(VARIANT), POINTER(VARIANT) # some commonly used VARIANT instances VARIANT.null = VARIANT(None) VARIANT.empty = VARIANT() VARIANT.missing = v = VARIANT() v.vt = VT_ERROR v._.VT_I4 = 0x80020004 del v _carg_obj = type(byref(c_int())) from _ctypes import Array as _CArrayType @Patch(POINTER(VARIANT)) class _(object): # Override the default .from_param classmethod of POINTER(VARIANT). # This allows to pass values which can be stored in VARIANTs as # function parameters declared as POINTER(VARIANT). See # InternetExplorer's Navigate2() method, or Word's Close() method, for # examples. def from_param(cls, arg): # accept POINTER(VARIANT) instance if isinstance(arg, POINTER(VARIANT)): return arg # accept byref(VARIANT) instance if isinstance(arg, _carg_obj) and isinstance(arg._obj, VARIANT): return arg # accept VARIANT instance if isinstance(arg, VARIANT): return byref(arg) if isinstance(arg, _CArrayType) and arg._type_ is VARIANT: # accept array of VARIANTs return arg # anything else which can be converted to a VARIANT. return byref(VARIANT(arg)) from_param = classmethod(from_param) def __setitem__(self, index, value): # This is to support the same sematics as a pointer instance: # variant[0] = value self[index].value = value ################################################################ # interfaces, structures, ... class IEnumVARIANT(IUnknown): _iid_ = GUID('{00020404-0000-0000-C000-000000000046}') _idlflags_ = ['hidden'] _dynamic = False def __iter__(self): return self def __next__(self): item, fetched = self.Next(1) if fetched: return item raise StopIteration def __getitem__(self, index): self.Reset() # Does not yet work. ## if isinstance(index, slice): ## self.Skip(index.start or 0) ## return self.Next(index.stop or sys.maxint) self.Skip(index) item, fetched = self.Next(1) if fetched: return item raise IndexError def Next(self, celt): fetched = c_ulong() if celt == 1: v = VARIANT() self.__com_Next(celt, v, fetched) return v._get_value(dynamic=self._dynamic), fetched.value array = (VARIANT * celt)() self.__com_Next(celt, array, fetched) result = [v._get_value(dynamic=self._dynamic) for v in array[:fetched.value]] for v in array: v.value = None return result IEnumVARIANT._methods_ = [ COMMETHOD([], HRESULT, 'Next', ( ['in'], c_ulong, 'celt' ), ( ['out'], POINTER(VARIANT), 'rgvar' ), ( ['out'], POINTER(c_ulong), 'pceltFetched' )), COMMETHOD([], HRESULT, 'Skip', ( ['in'], c_ulong, 'celt' )), COMMETHOD([], HRESULT, 'Reset'), COMMETHOD([], HRESULT, 'Clone', ( ['out'], POINTER(POINTER(IEnumVARIANT)), 'ppenum' )), ] ##from _ctypes import VARIANT_set ##import new ##VARIANT.value = property(VARIANT._get_value, new.instancemethod(VARIANT_set, None, VARIANT)) class tagEXCEPINFO(Structure): def __repr__(self): return "" % \ ((self.wCode, self.bstrSource, self.bstrDescription, self.bstrHelpFile, self.dwHelpContext, self.pfnDeferredFillIn, self.scode),) tagEXCEPINFO._fields_ = [ ('wCode', WORD), ('wReserved', WORD), ('bstrSource', BSTR), ('bstrDescription', BSTR), ('bstrHelpFile', BSTR), ('dwHelpContext', DWORD), ('pvReserved', c_void_p), ## ('pfnDeferredFillIn', WINFUNCTYPE(HRESULT, POINTER(tagEXCEPINFO))), ('pfnDeferredFillIn', c_void_p), ('scode', SCODE), ] EXCEPINFO = tagEXCEPINFO class tagDISPPARAMS(Structure): _fields_ = [ # C:/Programme/gccxml/bin/Vc71/PlatformSDK/oaidl.h 696 ('rgvarg', POINTER(VARIANTARG)), ('rgdispidNamedArgs', POINTER(DISPID)), ('cArgs', UINT), ('cNamedArgs', UINT), ] def __del__(self): if self._b_needsfree_: for i in range(self.cArgs): self.rgvarg[i].value = None DISPPARAMS = tagDISPPARAMS DISPID_VALUE = 0 DISPID_UNKNOWN = -1 DISPID_PROPERTYPUT = -3 DISPID_NEWENUM = -4 DISPID_EVALUATE = -5 DISPID_CONSTRUCTOR = -6 DISPID_DESTRUCTOR = -7 DISPID_COLLECT = -8 class IDispatch(IUnknown): _iid_ = GUID("{00020400-0000-0000-C000-000000000046}") _methods_ = [ COMMETHOD([], HRESULT, 'GetTypeInfoCount', (['out'], POINTER(UINT) ) ), COMMETHOD([], HRESULT, 'GetTypeInfo', (['in'], UINT, 'index'), (['in'], LCID, 'lcid', 0), ## Normally, we would declare this parameter in this way: ## (['out'], POINTER(POINTER(ITypeInfo)) ) ), ## but we cannot import comtypes.typeinfo at the top level (recursive imports!). (['out'], POINTER(POINTER(IUnknown)) ) ), STDMETHOD(HRESULT, 'GetIDsOfNames', [POINTER(IID), POINTER(c_wchar_p), UINT, LCID, POINTER(DISPID)]), STDMETHOD(HRESULT, 'Invoke', [DISPID, POINTER(IID), LCID, WORD, POINTER(DISPPARAMS), POINTER(VARIANT), POINTER(EXCEPINFO), POINTER(UINT)]), ] def GetTypeInfo(self, index, lcid=0): """Return type information. Index 0 specifies typeinfo for IDispatch""" import comtypes.typeinfo result = self._GetTypeInfo(index, lcid) return result.QueryInterface(comtypes.typeinfo.ITypeInfo) def GetIDsOfNames(self, *names, **kw): """Map string names to integer ids.""" lcid = kw.pop("lcid", 0) assert not kw arr = (c_wchar_p * len(names))(*names) ids = (DISPID * len(names))() self.__com_GetIDsOfNames(riid_null, arr, len(names), lcid, ids) return ids[:] def _invoke(self, memid, invkind, lcid, *args): var = VARIANT() argerr = c_uint() dp = DISPPARAMS() if args: array = (VARIANT * len(args))() for i, a in enumerate(args[::-1]): array[i].value = a dp.cArgs = len(args) if invkind in (DISPATCH_PROPERTYPUT, DISPATCH_PROPERTYPUTREF): dp.cNamedArgs = 1 dp.rgdispidNamedArgs = pointer(DISPID(DISPID_PROPERTYPUT)) dp.rgvarg = array self.__com_Invoke(memid, riid_null, lcid, invkind, dp, var, None, argerr) return var._get_value(dynamic=True) def Invoke(self, dispid, *args, **kw): """Invoke a method or property.""" # Memory management in Dispatch::Invoke calls: # http://msdn.microsoft.com/library/en-us/automat/htm/chap5_4x2q.asp # Quote: # The *CALLING* code is responsible for releasing all strings and # objects referred to by rgvarg[ ] or placed in *pVarResult. # # For comtypes this is handled in DISPPARAMS.__del__ and VARIANT.__del__. _invkind = kw.pop("_invkind", 1) # DISPATCH_METHOD _lcid = kw.pop("_lcid", 0) if kw: raise ValueError("named parameters not yet implemented") result = VARIANT() excepinfo = EXCEPINFO() argerr = c_uint() if _invkind in (DISPATCH_PROPERTYPUT, DISPATCH_PROPERTYPUTREF): # propput array = (VARIANT * len(args))() for i, a in enumerate(args[::-1]): array[i].value = a dp = DISPPARAMS() dp.cArgs = len(args) dp.cNamedArgs = 1 dp.rgvarg = array dp.rgdispidNamedArgs = pointer(DISPID(DISPID_PROPERTYPUT)) else: array = (VARIANT * len(args))() for i, a in enumerate(args[::-1]): array[i].value = a dp = DISPPARAMS() dp.cArgs = len(args) dp.cNamedArgs = 0 dp.rgvarg = array try: self.__com_Invoke(dispid, riid_null, _lcid, _invkind, byref(dp), byref(result), byref(excepinfo), byref(argerr)) except COMError as err: (hresult, text, details) = err.args if hresult == DISP_E_EXCEPTION: details = (excepinfo.bstrDescription, excepinfo.bstrSource, excepinfo.bstrHelpFile, excepinfo.dwHelpContext, excepinfo.scode) raise COMError(hresult, text, details) elif hresult == DISP_E_PARAMNOTFOUND: # MSDN says: You get the error DISP_E_PARAMNOTFOUND # when you try to set a property and you have not # initialized the cNamedArgs and rgdispidNamedArgs # elements of your DISPPARAMS structure. # # So, this looks like a bug. raise COMError(hresult, text, argerr.value) elif hresult == DISP_E_TYPEMISMATCH: # MSDN: One or more of the arguments could not be # coerced. # # Hm, should we raise TypeError, or COMError? raise COMError(hresult, text, ("TypeError: Parameter %s" % (argerr.value + 1), args)) raise return result._get_value(dynamic=True) # XXX Would separate methods for _METHOD, _PROPERTYGET and _PROPERTYPUT be better? ################################################################ # safearrays # XXX Only one-dimensional arrays are currently implemented # map ctypes types to VARTYPE values _arraycode_to_vartype = { "d": VT_R8, "f": VT_R4, "l": VT_I4, "i": VT_INT, "h": VT_I2, "b": VT_I1, "I": VT_UINT, "L": VT_UI4, "H": VT_UI2, "B": VT_UI1, } _ctype_to_vartype = { c_byte: VT_I1, c_ubyte: VT_UI1, c_short: VT_I2, c_ushort: VT_UI2, c_long: VT_I4, c_ulong: VT_UI4, c_float: VT_R4, c_double: VT_R8, c_longlong: VT_I8, c_ulonglong: VT_UI8, VARIANT_BOOL: VT_BOOL, BSTR: VT_BSTR, VARIANT: VT_VARIANT, # SAFEARRAY(VARIANT *) # # It is unlear to me if this is allowed or not. Apparently there # are typelibs that define such an argument type, but it may be # that these are buggy. # # Point is that SafeArrayCreateEx(VT_VARIANT|VT_BYREF, ..) fails. # The MSDN docs for SafeArrayCreate() have a notice that neither # VT_ARRAY not VT_BYREF may be set, this notice is missing however # for SafeArrayCreateEx(). # # We have this code here to make sure that comtypes can import # such a typelib, although calling ths method will fail because # such an array cannot be created. POINTER(VARIANT): VT_BYREF|VT_VARIANT, # This is needed to import Esri ArcObjects (esriSystem.olb). POINTER(BSTR): VT_BYREF|VT_BSTR, # These are not yet implemented: ## POINTER(IUnknown): VT_UNKNOWN, ## POINTER(IDispatch): VT_DISPATCH, } _vartype_to_ctype = {} for c, v in _ctype_to_vartype.items(): _vartype_to_ctype[v] = c _vartype_to_ctype[VT_INT] = _vartype_to_ctype[VT_I4] _vartype_to_ctype[VT_UINT] = _vartype_to_ctype[VT_UI4] _ctype_to_vartype[c_char] = VT_UI1 try: from comtypes.safearray import _midlSAFEARRAY except (ImportError, AttributeError): pass