PageRenderTime 36ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/docs/source/server.rst

https://gitlab.com/hoiwai930/comtypes
ReStructuredText | 325 lines | 233 code | 92 blank | 0 comment | 0 complexity | a70307ffcde9316d703801d46d357144 MD5 | raw file
  1. #########################
  2. COM servers with comtypes
  3. #########################
  4. |comtypes| is a *pure Python* COM package based on the ctypes_ ffi
  5. foreign function library. **ctypes** is included in Python 2.5 and
  6. later, it is also available for Python 2.4 as separate download.
  7. The |comtypes| package makes it easy to access and implement both
  8. custom and dispatch based COM interfaces.
  9. This document describes |comtypes| version 0.5.
  10. .. contents::
  11. Implementing a simple COM object
  12. ********************************
  13. To implement a COM server object in comtypes you need to write a type
  14. library describing the coclass, the interface(s) that the object
  15. implements, and (optional) the event interface that the object
  16. supports. Also you have to write a Python module that defines a class
  17. which implements the object itself.
  18. We will present a short example here that does actually work.
  19. Define the COM interface
  20. ++++++++++++++++++++++++
  21. Start writing an IDL file. It is a good idea to define ``dual``
  22. interfaces, and only use automation compatible data types.
  23. .. sourcecode:: c
  24. import "oaidl.idl";
  25. import "ocidl.idl";
  26. [
  27. uuid(xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx),
  28. dual,
  29. oleautomation
  30. ]
  31. interface IMyInterface : IDispatch {
  32. HRESULT MyMethod([in] INT a, [in] INT b, [out, retval] INT *presult);
  33. }
  34. [
  35. uuid(xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)
  36. ]
  37. library MyTypeLib
  38. {
  39. importlib("stdole2.tlb");
  40. [uuid(xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)]
  41. coclass MyObject {
  42. [default] interface IMyInterface;
  43. };
  44. };
  45. Please note that you must replace the 'xxxx' placeholders in the
  46. section above with separate GUIDs that you must generate yourself.
  47. You can use Python to generate unique GUIDs by running this in a
  48. windows console:
  49. .. sourcecode:: shell
  50. C:\> python -c "from comtypes import GUID; print GUID.create_new()"
  51. {26F87CEB-603A-4FFE-8865-DB67A9E3A308}
  52. C:\>
  53. The IDL file should now be compiled with the Microsoft MIDL compiler to a
  54. TLB type library file.
  55. Implement the class
  56. +++++++++++++++++++
  57. Generate and import the wrapper module (which is named after the
  58. ``library`` statement in the IDL file), and create a subclass of the
  59. ``MyObject`` coclass.
  60. Most required class attributes are already defined in the typelib
  61. wrapper file. You must at least add attributes for registration that
  62. are not in the type library.
  63. .. sourcecode:: python
  64. import comtypes
  65. import comtypes.server.localserver
  66. from comtypes.client import GetModule
  67. # generate wrapper code for the type library, this needs
  68. # to be done only once (but also each time the IDL file changes)
  69. GetModule("mytypelib.tlb")
  70. from comtypes.gen.MyTypeLib import MyObject
  71. class MyObjectImpl(MyObject):
  72. # registry entries
  73. _reg_threading_ = "Both"
  74. _reg_progid_ = "MyTypeLib.MyObject.1"
  75. _reg_novers_progid_ = "MyTypeLib.MyObject"
  76. _reg_desc_ = "Simple COM server for testing"
  77. _reg_clsctx_ = comtypes.CLSCTX_INPROC_SERVER | comtypes.CLSCTX_LOCAL_SERVER
  78. _regcls_ = comtypes.server.localserver.REGCLS_MULTIPLEUSE
  79. The meaning of the attributes:
  80. ``_reg_threading_`` must be set to "Both", "Free", or "Apartment".
  81. It specifies the apartment model in which the server runs.
  82. ``_reg_progid_`` and ``_reg_novers_progid`` are optional short
  83. names that can later be used to specify your object, instead of
  84. the CLSID in type library. Typically the type library name plus
  85. the coclass name plus a version number are combined to form the
  86. progid, and the type library name plus the coclass name are
  87. combined to form the version independend progid.
  88. ``_reg_desc_`` is the (optional) name of the coclass.
  89. The ``_reg_clsctx_`` constant specifies in which contexts the com
  90. server can operate.
  91. The optional ``_regcls_`` constant is only used for com objects
  92. that run in their own process, see the MSDN docs for more info.
  93. In comtypes, several REGCLS values are defined in the
  94. ``comtyper.server.localserver`` module.
  95. You do not yet implement any methods on the class, because basic
  96. functionality is already present.
  97. Register and run the object for the first time
  98. ++++++++++++++++++++++++++++++++++++++++++++++
  99. A COM object must by registered with Windows, and will also be started
  100. at runtime by Windows. This magic, on the |comtypes| side, is done by
  101. the ``comtypes.server.register.UseCommandLine`` function. You should
  102. call it in the ``if __name__ == "__main__"`` block of your script,
  103. with the ``MyObjectImpl`` class:
  104. .. sourcecode:: python
  105. if __name__ == "__main__":
  106. from comtypes.server.register import UseCommandLine
  107. UseCommandLine(MyObjectImpl)
  108. You should now run your script with a ``/regserver`` command line
  109. option, this will write information about your object into the Windows
  110. registry:
  111. .. sourcecode:: shell
  112. C:\> python myserver.py /regserver
  113. If you have the Microsoft ``OLEVIEW`` utility, you can now open the
  114. "All Objects" item, and look for the "Simple COM server for testing"
  115. object. If everything works well, you can even create an instance of
  116. your COM object by double clicking the entry, and you will see that
  117. the object implements quite some interfaces already.
  118. You can also create an instance of the object with comtypes:
  119. .. sourcecode:: pycon
  120. >>> from comtypes.client import CreateObject
  121. >>> x = CreateObject("MyTypelib.MyObject")
  122. >>> print x
  123. <POINTER(IMyInterface) ptr=0x1216328 at 1216620>
  124. >>>
  125. Of course calling a method does not yet work since it is not
  126. implemented in the server script:
  127. .. sourcecode:: pycon
  128. >>> x.MyMethod(a, 2)
  129. Traceback (most recent call last):
  130. File "<stdin>", line 1, in <module>
  131. _ctypes.COMError: COMError(0x80004001, 'Nicht implementiert', (None, None, None, 0, None))
  132. >>>
  133. Implementing COM methods
  134. ++++++++++++++++++++++++
  135. NOTE: The documentation in this section is also valid for writing
  136. COM event handlers!
  137. In the IDL file, the method signature is defined like this:
  138. .. sourcecode:: c
  139. HRESULT MyMethod([in] INT a, [in] INT b, [out, retval] INT *presult);
  140. So, this method takes two integers and returns a third one, writing
  141. the latter into a pointer.
  142. You must add e Python method to the class ``MyObject`` that implements
  143. this behaviour.
  144. Determining the method name
  145. ---------------------------
  146. The method implementing the ``IMyInterface.MyMethod`` can either be
  147. named ``IMyInterface_MyMethod`` or ``MyMethod``. Choose a name that
  148. does not conflict with other methods of the class, and that serves
  149. your personal naming conventions.
  150. In |comtypes|, there are two ways to implement COM server methods.
  151. You can choose between a 'low level' and a 'high level' implementation
  152. strategy, on a method by method basis (the names 'Low level' and 'high
  153. level' are probably misleading a bit, suggestions for better names
  154. would be welcomed). |comtypes| uses different calling conventions for
  155. 'low level' and 'high level' method implementations.
  156. |comtypes| inspects the method for the name of the second parameter,
  157. just after the ``self`` parameter:
  158. **If the second parameter is present and is named ``this`` then the
  159. low level calling convention is used. If the second parameter is
  160. not present, or is not named ``this``, then the high level calling
  161. convention is used.**
  162. Low level implementation
  163. ------------------------
  164. A low-level method implementation is called with the following arguments:
  165. - the usual ``self`` argument
  166. - for the ``this`` argument either ``None`` is passed, or the address
  167. of the COM object itself as an integer. The value of it can usually
  168. and should be ignored.
  169. - any other arguments listed in the IDL method signature.
  170. [in] parameters from the method signature are usually converted to
  171. native Python objects, if possible. For [out] or [out, retval]
  172. parameters ctypes pointer instances are passed, you are required to
  173. put the result value into the pointer(s).
  174. A low level method implementation must return a numerical HRESULT
  175. value, which specifies a success or failure code for the operation.
  176. The usual ``S_OK`` success code has a value of zero, but for
  177. convenience you can also return None instead.
  178. So, a sample low-level implementation for ``MyMethod`` for our object
  179. would be this, assuming we want to return the sum of the two [in]
  180. parameters:
  181. .. sourcecode:: python
  182. ...
  183. class MyObjectImpl(MyObject):
  184. ...
  185. # Note the 'this' second parameter
  186. def MyMethod(self, this, a, b, presult):
  187. presult[0] = a + b
  188. return 0
  189. High level implementation
  190. -------------------------
  191. A high-level method implementation is called with the following parameters:
  192. - the usual ``self`` argument
  193. - the [in] parameters from the IDL method signature.
  194. If there is a single [out] or [out, retval] parameter, then the method
  195. must return the result value; if there are more than one [out] or
  196. [out, retval] parameters, then a tuple containing the correct number
  197. must be returned. If there are no [out] or [out, retval] parameters,
  198. the return value does not matter and is ignored.
  199. A sample high-level implementation for ``MyMethod`` is this:
  200. .. sourcecode:: python
  201. class MyObjectImpl(MyObject):
  202. ...
  203. # Note: NO second 'this' parameter
  204. def MyMethod(self, a, b):
  205. return a + b
  206. Choosing between low-level or high-level implementation
  207. -------------------------------------------------------
  208. Both implementation strategies have their own advantages and
  209. disadvantages, so you should choose between them on a case by case
  210. basis:
  211. Low-level makes it easy to return special HRESULT values in the case
  212. that your object requires it.
  213. High-level is usually easier to write, and is compatible with the
  214. normal calling convention that Python also chooses. However, it is
  215. more difficult to specify the HRESULT value to return in case you want
  216. to communicate error codes to the caller.
  217. Run the object again and test the method
  218. ++++++++++++++++++++++++++++++++++++++++
  219. We can now create the object and test the implemented method:
  220. .. sourcecode:: pycon
  221. >>> from comtypes.client import CreateObject
  222. >>> x = CreateObject("MyTypelib.MyObject")
  223. >>> print x
  224. <POINTER(IMyInterface) ptr=0x1216328 at 1216620>
  225. >>> print x.MyMethod(42, 5)
  226. 47
  227. >>>
  228. More details on COM objects
  229. ***************************
  230. To be written...
  231. .. |comtypes| replace:: **comtypes**
  232. .. _`WMI monikers`: http://www.microsoft.com/technet/scriptcenter/guide/sas_wmi_jgfx.mspx?mfr=true
  233. .. _ctypes: http://starship.python.net/crew/theller/ctypes