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 _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)