""" General Server side utilities """ import pythoncom import winerror from . import policy from .exception import COMException def wrap(ob, iid=None, usePolicy=None, useDispatcher=None): """Wraps an object in a PyGDispatch gateway. Returns a client side PyI{iid} interface. Interface and gateway support must exist for the specified IID, as the QueryInterface() method is used. """ if usePolicy is None: usePolicy = policy.DefaultPolicy if useDispatcher == 1: # True will also work here. import win32com.server.dispatcher useDispatcher = win32com.server.dispatcher.DefaultDebugDispatcher if useDispatcher is None or useDispatcher == 0: ob = usePolicy(ob) else: ob = useDispatcher(usePolicy, ob) # get a PyIDispatch, which interfaces to PyGDispatch ob = pythoncom.WrapObject(ob) if iid is not None: ob = ob.QueryInterface(iid) # Ask the PyIDispatch if it supports it? return ob def unwrap(ob): """Unwraps an interface. Given an interface which wraps up a Gateway, return the object behind the gateway. """ ob = pythoncom.UnwrapObject(ob) # see if the object is a dispatcher if hasattr(ob, "policy"): ob = ob.policy return ob._obj_ class ListEnumerator: """A class to expose a Python sequence as an EnumVARIANT. Create an instance of this class passing a sequence (list, tuple, or any sequence protocol supporting object) and it will automatically support the EnumVARIANT interface for the object. See also the @NewEnum@ function, which can be used to turn the instance into an actual COM server. """ _public_methods_ = ["Next", "Skip", "Reset", "Clone"] def __init__(self, data, index=0, iid=pythoncom.IID_IEnumVARIANT): self._list_ = data self.index = index self._iid_ = iid def _query_interface_(self, iid): if iid == self._iid_: return 1 def Next(self, count): result = self._list_[self.index : self.index + count] self.Skip(count) return result def Skip(self, count): end = self.index + count if end > len(self._list_): end = len(self._list_) self.index = end def Reset(self): self.index = 0 def Clone(self): return self._wrap(self.__class__(self._list_, self.index)) def _wrap(self, ob): return wrap(ob) class ListEnumeratorGateway(ListEnumerator): """A List Enumerator which wraps a sequence's items in gateways. If a sequence contains items (objects) that have not been wrapped for return through the COM layers, then a ListEnumeratorGateway can be used to wrap those items before returning them (from the Next() method). See also the @ListEnumerator@ class and the @NewEnum@ function. """ def Next(self, count): result = self._list_[self.index : self.index + count] self.Skip(count) return map(self._wrap, result) def NewEnum( seq, cls=ListEnumerator, iid=pythoncom.IID_IEnumVARIANT, usePolicy=None, useDispatcher=None, ): """Creates a new enumerator COM server. This function creates a new COM Server that implements the IID_IEnumVARIANT interface. A COM server that can enumerate the passed in sequence will be created, then wrapped up for return through the COM framework. Optionally, a custom COM server for enumeration can be passed (the default is @ListEnumerator@), and the specific IEnum interface can be specified. """ ob = cls(seq, iid=iid) return wrap(ob, iid, usePolicy=usePolicy, useDispatcher=useDispatcher) class Collection: "A collection of VARIANT values." _public_methods_ = ["Item", "Count", "Add", "Remove", "Insert"] def __init__(self, data=None, readOnly=0): if data is None: data = [] self.data = data # disable Add/Remove if read-only. note that we adjust _public_methods_ # on this instance only. if readOnly: self._public_methods_ = ["Item", "Count"] # This method is also used as the "default" method. # Thus "print ob" will cause this to be called with zero # params. Handle this slightly more elegantly here. # Ideally the policy should handle this. def Item(self, *args): if len(args) != 1: raise COMException(scode=winerror.DISP_E_BADPARAMCOUNT) try: return self.data[args[0]] except IndexError as desc: raise COMException(scode=winerror.DISP_E_BADINDEX, desc=str(desc)) _value_ = Item def Count(self): return len(self.data) def Add(self, value): self.data.append(value) def Remove(self, index): try: del self.data[index] except IndexError as desc: raise COMException(scode=winerror.DISP_E_BADINDEX, desc=str(desc)) def Insert(self, index, value): try: index = int(index) except (ValueError, TypeError): raise COMException(scode=winerror.DISP_E_TYPEMISMATCH) self.data.insert(index, value) def _NewEnum(self): return NewEnum(self.data) def NewCollection(seq, cls=Collection): """Creates a new COM collection object This function creates a new COM Server that implements the common collection protocols, including enumeration. (_NewEnum) A COM server that can enumerate the passed in sequence will be created, then wrapped up for return through the COM framework. Optionally, a custom COM server for enumeration can be passed (the default is @Collection@). """ return pythoncom.WrapObject( policy.DefaultPolicy(cls(seq)), pythoncom.IID_IDispatch, pythoncom.IID_IDispatch ) class FileStream: _public_methods_ = ["Read", "Write", "Clone", "CopyTo", "Seek"] _com_interfaces_ = [pythoncom.IID_IStream] def __init__(self, file): self.file = file def Read(self, amount): return self.file.read(amount) def Write(self, data): self.file.write(data) return len(data) def Clone(self): return self._wrap(self.__class__(self.file)) def CopyTo(self, dest, cb): data = self.file.read(cb) cbread = len(data) dest.Write(data) ## ??? Write does not currently return the length ??? return cbread, cbread def Seek(self, offset, origin): # how convient that the 'origin' values are the same as the CRT :) self.file.seek(offset, origin) return self.file.tell() def _wrap(self, ob): return wrap(ob)