def variant_array(value): from ctypes import (POINTER, Structure, byref, cast, c_long, c_float, c_double, memmove, pointer, sizeof) from comtypes import _safearray, IUnknown, com_interface_registry from comtypes.automation import VARIANT, VT_VARIANT, VT_SAFEARRAY, VT_ARRAY, tagVARIANT, VT_DISPATCH from comtypes.safearray import _midlSAFEARRAY vartype = VT_DISPATCH itemtype = VARIANT cls = _midlSAFEARRAY(itemtype) rgsa = (_safearray.SAFEARRAYBOUND * 1)() rgsa[0].cElements = len(value) rgsa[0].lBound = 0 pa = _safearray.SafeArrayCreateEx(vartype, 1, rgsa, None) pa = cast(pa, cls) # Now, fill the data in: ptr = POINTER(itemtype)() # pointer to the item values _safearray.SafeArrayAccessData(pa, byref(ptr)) try: for i, a in enumerate(value): ptr[i] = a finally: _safearray.SafeArrayUnaccessData(pa) var = tagVARIANT() memmove(byref(var._), byref(pa), sizeof(pa)) var.vt = VT_ARRAY | pa._vartype_ return var
def create_from_linearized_matrix(value): from ctypes import (POINTER, Structure, byref, cast, c_long, c_float, c_double, memmove, pointer, sizeof) from comtypes import _safearray, IUnknown, com_interface_registry from comtypes.automation import VARIANT, VT_R8, VT_ARRAY, tagVARIANT from comtypes.safearray import _midlSAFEARRAY addr, n = value.buffer_info() vartype = VT_R8 itemtype = c_double cls = _midlSAFEARRAY(itemtype) matrixsize = 4 rgsa = (_safearray.SAFEARRAYBOUND * 2)() rgsa[0].cElements = matrixsize rgsa[0].lBound = 0 rgsa[1].cElements = matrixsize rgsa[1].lBound = 0 pa = _safearray.SafeArrayCreateEx(vartype, 2, rgsa, None) pa = cast(pa, cls) # Now, fill the data in: ptr = POINTER(itemtype)() # pointer to the item values _safearray.SafeArrayAccessData(pa, byref(ptr)) try: nbytes = matrixsize**2 * sizeof(itemtype) memmove(ptr, addr, nbytes) finally: _safearray.SafeArrayUnaccessData(pa) var = tagVARIANT() memmove(byref(var._), byref(pa), sizeof(pa)) var.vt = VT_ARRAY | pa._vartype_ return var
def _get_elements_raw(self, num_elements): """Returns a flat list or ndarray containing ALL elements in the safearray.""" from comtypes.automation import VARIANT # XXX Not sure this is true: # For VT_UNKNOWN and VT_DISPATCH, we should retrieve the # interface iid by SafeArrayGetIID(). ptr = POINTER(self._itemtype_)() # container for the values _safearray.SafeArrayAccessData(self, byref(ptr)) try: if self._itemtype_ == VARIANT: # We have to loop over each item, so we get no # speedup by creating an ndarray here. return [i.value for i in ptr[:num_elements]] elif issubclass(self._itemtype_, POINTER(IUnknown)): iid = _safearray.SafeArrayGetIID(self) itf = com_interface_registry[str(iid)] # COM interface pointers retrieved from array # must be AddRef()'d if non-NULL. elems = ptr[:num_elements] result = [] # We have to loop over each item, so we get no # speedup by creating an ndarray here. for p in elems: if bool(p): p.AddRef() result.append(p.QueryInterface(itf)) else: # return a NULL-interface pointer. result.append(POINTER(itf)()) return result else: # If the safearray element are NOT native python # objects, the containing safearray must be kept # alive until all the elements are destroyed. if not issubclass(self._itemtype_, Structure): # Create an ndarray if requested. This is where # we can get the most speed-up. # XXX Only try to convert types known to # numpy.ctypeslib. if (safearray_as_ndarray and self._itemtype_ in numpy.ctypeslib._typecodes.values()): arr = numpy.ctypeslib.as_array(ptr, (num_elements,)) return arr.copy() return ptr[:num_elements] def keep_safearray(v): # Simply keeping a reference to self does not keep the # internal addresses of BSTR safe and strings will be # overwritten. A copy of the bytes solves the problem v1 = v.__class__() memmove(byref(v1), byref(v), sizeof(v)) return v1 return [keep_safearray(x) for x in ptr[:num_elements]] finally: _safearray.SafeArrayUnaccessData(self)
def create_from_ndarray(cls, value, extra, lBound=0): from comtypes.automation import VARIANT #c:/python25/lib/site-packages/numpy/ctypeslib.py numpy = __import__("numpy.ctypeslib") # If processing VARIANT, makes sure the array type is correct. if cls._itemtype_ is VARIANT: if value.dtype != npsupport.VARIANT_dtype: value = _ndarray_to_variant_array(value) else: ai = value.__array_interface__ if ai["version"] != 3: raise TypeError( "only __array_interface__ version 3 supported") if cls._itemtype_ != numpy.ctypeslib._typecodes[ai["typestr"]]: raise TypeError("Wrong array item type") # SAFEARRAYs have Fortran order; convert the numpy array if needed if not value.flags.f_contiguous: value = numpy.array(value, order="F") # For VT_UNKNOWN or VT_DISPATCH, extra must be a pointer to # the GUID of the interface. # # For VT_RECORD, extra must be a pointer to an IRecordInfo # describing the record. rgsa = (_safearray.SAFEARRAYBOUND * value.ndim)() nitems = 1 for i, d in enumerate(value.shape): nitems *= d rgsa[i].cElements = d rgsa[i].lBound = lBound pa = _safearray.SafeArrayCreateEx( cls._vartype_, value.ndim, # cDims rgsa, # rgsaBound extra) # pvExtra if not pa: if cls._vartype_ == VT_RECORD and extra is None: raise TypeError( "Cannot create SAFEARRAY type VT_RECORD without IRecordInfo." ) # Hm, there may be other reasons why the creation fails... raise MemoryError() # We now have a POINTER(tagSAFEARRAY) instance which we must cast # to the correct type: pa = cast(pa, cls) # Now, fill the data in: ptr = POINTER(cls._itemtype_)() # pointer to the item values _safearray.SafeArrayAccessData(pa, byref(ptr)) try: nbytes = nitems * sizeof(cls._itemtype_) memmove(ptr, value.ctypes.data, nbytes) finally: _safearray.SafeArrayUnaccessData(pa) return pa
def create(cls, value, extra=None): """Create a POINTER(SAFEARRAY_...) instance of the correct type; value is an object containing the items to store. Python lists, tuples, and array.array instances containing compatible item types can be passed to create one-dimensional arrays. To create multidimensional arrys, numpy arrays must be passed. """ if "numpy" in sys.modules: numpy = sys.modules["numpy"] if isinstance(value, numpy.ndarray): return cls.create_from_ndarray(value, extra) # For VT_UNKNOWN or VT_DISPATCH, extra must be a pointer to # the GUID of the interface. # # For VT_RECORD, extra must be a pointer to an IRecordInfo # describing the record. # XXX How to specify the lbound (3. parameter to CreateVectorEx)? # XXX How to write tests for lbound != 0? pa = _safearray.SafeArrayCreateVectorEx(cls._vartype_, 0, len(value), extra) if not pa: if cls._vartype_ == VT_RECORD and extra is None: raise TypeError( "Cannot create SAFEARRAY type VT_RECORD without IRecordInfo." ) # Hm, there may be other reasons why the creation fails... raise MemoryError() # We now have a POINTER(tagSAFEARRAY) instance which we must cast # to the correct type: pa = cast(pa, cls) # Now, fill the data in: ptr = POINTER(cls._itemtype_)() # container for the values _safearray.SafeArrayAccessData(pa, byref(ptr)) try: if isinstance(value, array.array): addr, n = value.buffer_info() nbytes = len(value) * sizeof(cls._itemtype_) memmove(ptr, addr, nbytes) else: for index, item in enumerate(value): ptr[index] = item finally: _safearray.SafeArrayUnaccessData(pa) return pa
def set_variant_matrix(self, value): if not self.is_nested_iterable(value): raise TypeError('Input data is not nested list/tuple.') if self.is_ragged(value): raise TypeError('Input data should not be a ragged array.') num_row = len(value) num_col = len(value[0]) variant = VARIANT() _VariantClear(variant) # Clear the original data rgsa = (_safearray.SAFEARRAYBOUND * 2)() rgsa[0].cElements = num_row rgsa[0].lBound = 0 rgsa[1].cElements = num_col rgsa[1].lBound = 0 pa = _safearray.SafeArrayCreateEx( VT_VARIANT, 2, rgsa, # rgsaBound None) # pvExtra if not pa: raise MemoryError() ptr = POINTER(VARIANT)() # container for the values _safearray.SafeArrayAccessData(pa, byref(ptr)) try: # I have no idea why 2D safearray is column-major. index = 0 for n in range(num_col): for m in range(num_row): ptr[index] = value[m][n] index += 1 finally: _safearray.SafeArrayUnaccessData(pa) memmove(byref(variant._), byref(pa), sizeof(pa)) variant.vt = VT_ARRAY | VT_VARIANT return variant
def _get_elements_raw(self, num_elements): """Returns a flat list containing ALL elements in the safearray.""" from comtypes.automation import VARIANT # XXX Not sure this is true: # For VT_UNKNOWN and VT_DISPATCH, we should retrieve the # interface iid by SafeArrayGetIID(). ptr = POINTER(self._itemtype_)() # container for the values _safearray.SafeArrayAccessData(self, byref(ptr)) try: if self._itemtype_ == VARIANT: return [i.value for i in ptr[:num_elements]] elif issubclass(self._itemtype_, POINTER(IUnknown)): iid = _safearray.SafeArrayGetIID(self) itf = com_interface_registry[str(iid)] # COM interface pointers retrieved from array # must be AddRef()'d if non-NULL. elems = ptr[:num_elements] result = [] for p in elems: if bool(p): p.AddRef() result.append(p.QueryInterface(itf)) else: # return a NULL-interface pointer. result.append(POINTER(itf)()) return result else: # If the safearray element are NOT native python # objects, the containing safearray must be kept # alive until all the elements are destroyed. if not issubclass(self._itemtype_, Structure): # Creating and returning numpy arrays instead # of Python tuple from a safearray is a lot faster, # but only for large arrays because of a certain overhead. # Also, for backwards compatibility, some clients expect # a Python tuple - so there should be a way to select # what should be returned. How could that work? ## # A hack which would return numpy arrays ## # instead of Python lists. To be effective, ## # the result must not converted into a tuple ## # in the caller so there must be changes as ## # well! ## ## # Crude hack to create and attach an ## # __array_interface__ property to the ## # pointer instance ## array_type = ptr._type_ * num_elements ## if not hasattr(array_type, "__array_interface__"): ## import numpy.ctypeslib ## numpy.ctypeslib.prep_array(array_type) ## # use the array_type's __array_interface__, ... ## aif = array_type.__array_interface__.__get__(ptr) ## # overwrite the 'data' member so that it points to the ## # address we want to use ## aif["data"] = (cast(ptr, c_void_p).value, False) ## ptr.__array_interface__ = aif ## return numpy.array(ptr, copy=True) return ptr[:num_elements] def keep_safearray(v): v.__keepref = self return v return [keep_safearray(x) for x in ptr[:num_elements]] finally: _safearray.SafeArrayUnaccessData(self)