util.py 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. """ General Server side utilities
  2. """
  3. import pythoncom
  4. from . import policy
  5. import winerror
  6. from .exception import COMException
  7. def wrap(ob, iid=None, usePolicy=None, useDispatcher=None):
  8. """Wraps an object in a PyGDispatch gateway.
  9. Returns a client side PyI{iid} interface.
  10. Interface and gateway support must exist for the specified IID, as
  11. the QueryInterface() method is used.
  12. """
  13. if usePolicy is None:
  14. usePolicy = policy.DefaultPolicy
  15. if useDispatcher == 1: # True will also work here.
  16. import win32com.server.dispatcher
  17. useDispatcher = win32com.server.dispatcher.DefaultDebugDispatcher
  18. if useDispatcher is None or useDispatcher==0:
  19. ob = usePolicy(ob)
  20. else:
  21. ob = useDispatcher(usePolicy, ob)
  22. # get a PyIDispatch, which interfaces to PyGDispatch
  23. ob = pythoncom.WrapObject(ob)
  24. if iid is not None:
  25. ob = ob.QueryInterface(iid) # Ask the PyIDispatch if it supports it?
  26. return ob
  27. def unwrap(ob):
  28. """Unwraps an interface.
  29. Given an interface which wraps up a Gateway, return the object behind
  30. the gateway.
  31. """
  32. ob = pythoncom.UnwrapObject(ob)
  33. # see if the object is a dispatcher
  34. if hasattr(ob, 'policy'):
  35. ob = ob.policy
  36. return ob._obj_
  37. class ListEnumerator:
  38. """A class to expose a Python sequence as an EnumVARIANT.
  39. Create an instance of this class passing a sequence (list, tuple, or
  40. any sequence protocol supporting object) and it will automatically
  41. support the EnumVARIANT interface for the object.
  42. See also the @NewEnum@ function, which can be used to turn the
  43. instance into an actual COM server.
  44. """
  45. _public_methods_ = [ 'Next', 'Skip', 'Reset', 'Clone' ]
  46. def __init__(self, data, index=0, iid = pythoncom.IID_IEnumVARIANT):
  47. self._list_ = data
  48. self.index = index
  49. self._iid_ = iid
  50. def _query_interface_(self, iid):
  51. if iid == self._iid_:
  52. return 1
  53. def Next(self, count):
  54. result = self._list_[self.index:self.index+count]
  55. self.Skip(count)
  56. return result
  57. def Skip(self, count):
  58. end = self.index + count
  59. if end > len(self._list_):
  60. end = len(self._list_)
  61. self.index = end
  62. def Reset(self):
  63. self.index = 0
  64. def Clone(self):
  65. return self._wrap(self.__class__(self._list_, self.index))
  66. def _wrap(self, ob):
  67. return wrap(ob)
  68. class ListEnumeratorGateway(ListEnumerator):
  69. """A List Enumerator which wraps a sequence's items in gateways.
  70. If a sequence contains items (objects) that have not been wrapped for
  71. return through the COM layers, then a ListEnumeratorGateway can be
  72. used to wrap those items before returning them (from the Next() method).
  73. See also the @ListEnumerator@ class and the @NewEnum@ function.
  74. """
  75. def Next(self, count):
  76. result = self._list_[self.index:self.index+count]
  77. self.Skip(count)
  78. return map(self._wrap, result)
  79. def NewEnum(seq,
  80. cls=ListEnumerator,
  81. iid=pythoncom.IID_IEnumVARIANT,
  82. usePolicy=None,
  83. useDispatcher=None):
  84. """Creates a new enumerator COM server.
  85. This function creates a new COM Server that implements the
  86. IID_IEnumVARIANT interface.
  87. A COM server that can enumerate the passed in sequence will be
  88. created, then wrapped up for return through the COM framework.
  89. Optionally, a custom COM server for enumeration can be passed
  90. (the default is @ListEnumerator@), and the specific IEnum
  91. interface can be specified.
  92. """
  93. ob = cls(seq, iid=iid)
  94. return wrap(ob, iid, usePolicy=usePolicy, useDispatcher=useDispatcher)
  95. class Collection:
  96. "A collection of VARIANT values."
  97. _public_methods_ = [ 'Item', 'Count', 'Add', 'Remove', 'Insert' ]
  98. def __init__(self, data=None, readOnly=0):
  99. if data is None:
  100. data = [ ]
  101. self.data = data
  102. # disable Add/Remove if read-only. note that we adjust _public_methods_
  103. # on this instance only.
  104. if readOnly:
  105. self._public_methods_ = [ 'Item', 'Count' ]
  106. # This method is also used as the "default" method.
  107. # Thus "print ob" will cause this to be called with zero
  108. # params. Handle this slightly more elegantly here.
  109. # Ideally the policy should handle this.
  110. def Item(self, *args):
  111. if len(args) != 1:
  112. raise COMException(scode=winerror.DISP_E_BADPARAMCOUNT)
  113. try:
  114. return self.data[args[0]]
  115. except IndexError as desc:
  116. raise COMException(scode=winerror.DISP_E_BADINDEX, desc=str(desc))
  117. _value_ = Item
  118. def Count(self):
  119. return len(self.data)
  120. def Add(self, value):
  121. self.data.append(value)
  122. def Remove(self, index):
  123. try:
  124. del self.data[index]
  125. except IndexError as desc:
  126. raise COMException(scode=winerror.DISP_E_BADINDEX, desc=str(desc))
  127. def Insert(self, index, value):
  128. try:
  129. index = int(index)
  130. except (ValueError, TypeError):
  131. raise COMException(scode=winerror.DISP_E_TYPEMISMATCH)
  132. self.data.insert(index, value)
  133. def _NewEnum(self):
  134. return NewEnum(self.data)
  135. def NewCollection(seq, cls=Collection):
  136. """Creates a new COM collection object
  137. This function creates a new COM Server that implements the
  138. common collection protocols, including enumeration. (_NewEnum)
  139. A COM server that can enumerate the passed in sequence will be
  140. created, then wrapped up for return through the COM framework.
  141. Optionally, a custom COM server for enumeration can be passed
  142. (the default is @Collection@).
  143. """
  144. return pythoncom.WrapObject(policy.DefaultPolicy(cls(seq)),
  145. pythoncom.IID_IDispatch,
  146. pythoncom.IID_IDispatch)
  147. class FileStream:
  148. _public_methods_ = [ 'Read', 'Write', 'Clone', 'CopyTo', 'Seek' ]
  149. _com_interfaces_ = [ pythoncom.IID_IStream ]
  150. def __init__(self, file):
  151. self.file = file
  152. def Read(self, amount):
  153. return self.file.read(amount)
  154. def Write(self, data):
  155. self.file.write(data)
  156. return len(data)
  157. def Clone(self):
  158. return self._wrap(self.__class__(self.file))
  159. def CopyTo(self, dest, cb):
  160. data=self.file.read(cb)
  161. cbread=len(data)
  162. dest.Write(data) ## ??? Write does not currently return the length ???
  163. return cbread, cbread
  164. def Seek(self, offset, origin):
  165. # how convient that the 'origin' values are the same as the CRT :)
  166. self.file.seek(offset, origin)
  167. return self.file.tell()
  168. def _wrap(self, ob):
  169. return wrap(ob)