JacobThreading.html 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. <H1>COM Apartments in JACOB</H1>
  2. <H2>introduction</H2>
  3. The COM model for Threading differs from the Java model. In COM, each component can declare whether or not it support multi-threading.
  4. You can find some basic information about COM threading at:
  5. <ul>
  6. <li>
  7. <a href="http://www.execpc.com/~gopalan/com/com_threading.html">
  8. http://www.execpc.com/~gopalan/com/com_threading.html</a>
  9. </li>
  10. <li>
  11. <a href="www.microsoft.com/msj/0297/apartment/apartment.htm">
  12. www.microsoft.com/msj/0297/apartment/apartment.htm</a>
  13. </li>
  14. <li>
  15. <a href="http://www.cswl.com/whiteppr/white/multithreading.html">
  16. http://www.cswl.com/whiteppr/white/multithreading.html
  17. </a>
  18. </li>
  19. </ul>
  20. The term
  21. <b>Single Threaded Apartment (STA)</b> refers to a thread where all COM objects created in that thread are single-threaded.
  22. This can manifest itself in two ways:
  23. <br> Either all calls into that component are made from the same thread that created the component
  24. <br> OR any call that is made from another thread gets serialized by COM. This serialization of calls is done by using a Windows
  25. message loop and posting messages to a hidden window (I'm not kidding). The way COM achieves this is by requiring any other
  26. thread to make calls through a local Proxy object rather than the original object (more on this when we discuss the JACOB
  27. DispatchProxy class).
  28. <p></p>
  29. What does this mean for a Java application? If you are using a component that declares itself as
  30. <b>ThreadingModel "Apartment"</b> (you can find this out by looking in the registry under its CLSID), and you plan to create,
  31. use and destroy this component in one thread - then you are following the rules of an STA and you can declare the thread
  32. as an STA thread.
  33. <p></p>
  34. On the other hand, if you need to make method calls from another thread (e.g. in a servlet) then you have a few choices.
  35. Either you create the component in its own STA, by extending
  36. <code>com.jacob.com.STA</code>, and use the
  37. <code>com.jacob.com.DispatchProxy</code> class to pass the Dispatch pointer between threads, or you can declare your thread as an MTA thread. In that case, COM will
  38. make the cross-thread calls into the STA that is running your component. If you create an Apartment threaded component in
  39. the MTA, COM will automatically create an STA for you and put your component in there, and then marshall all the calls.
  40. <p></p>
  41. This brings us to the notion of a
  42. <b>Main STA</b>. COM requires that if there is any Apartment threaded component in your application, then the first STA created
  43. is tagged as the
  44. <b>Main STA</b>. COM uses the Main STA to create all the Apartment threaded components that are created from an MTA thread.
  45. The problem is that if you have already created an STA, then COM will pick that as the Main STA, and if you ever exit that
  46. thread - the whole application will exit.
  47. <H2>COM Threads in JACOB Prior to Version 1.7</H2>
  48. Up until version 1.7 of JACOB, there was only one model available in JACOB:
  49. <ul>
  50. <li>
  51. Before version 1.6: All threads were automatically initialized as STAs.
  52. </li>
  53. <li>
  54. In version 1.6: All threads were automatically initialized as MTAs.
  55. </li>
  56. </ul>
  57. <p></p>
  58. The reason for the change in default was that tagging a Java thread as an STA can cause problems. Any Java Swing application,
  59. as well as servlets and applets need to be able to make calls from multiple threads. If you try to make COM method calls
  60. across STA threads - it will fail!
  61. <p></p>
  62. In most cases, the default chosen by JACOB 1.6 (MTA) works fine, however there are some notable exceptions that have caused
  63. people grief. One such exception is in the case of MAPI. It turns out that if you try to create a MAPI object from an MTA
  64. thread - it simply fails and exits. This has caused some people to recompile JACOB 1.6 back with the STA default.
  65. <p></p>
  66. There is another problem with MTA threads: when you are using Apartment threaded components, we already noted that COM will
  67. create the components in the Main STA. If one doesn't exist, COM will create it. However, this means that
  68. <b>all</b> Apartment threaded components will be created in the
  69. <b>same STA</b>. This creates a bottleneck, and a dependency between unrelated components. Also, if that STA exits, then all
  70. components are destroyed and the application will likely crash.
  71. <H2>COM Threads in JACOB Version 1.7</H2>
  72. In Version 1.7 we have added finer grained control to allow the Java programmer to control how COM creates its components.
  73. Unfortunately, this means that you need to have a pretty good understanding of the dark and mystical subject of COM Apartments.
  74. There are a few different cases you need to consider:
  75. <H3>Default</H3>
  76. If you simply run code that was created in Version 1.6 and ignore the COM threading issue, then you will get the same behavior
  77. as in 1.6: Each java thread will be an MTA thread, and all Apartment threaded components will be created by COM in its own
  78. Main STA. This typically works for most applications (exceptions noted above).
  79. <H3>Create Your Own Apartment</H3>
  80. To declare an MTA thread use the following template:
  81. <br>
  82. <pre>
  83. <code>
  84. ComThread.InitMTA();
  85. ...
  86. ...
  87. ComThread.Release();
  88. </pre>
  89. </code>
  90. <br> If you want JACOB to create its own Main STA (rather than having COM choose an STA for you), then you should use:
  91. <br>
  92. <code>
  93. <pre>
  94. Thread 1:
  95. ComThread.InitMTA(true); // a true tells JACOB to create a Main STA
  96. ...
  97. ...
  98. ComThread.Release();
  99. ...
  100. Thread 2:
  101. ComThread.InitMTA();
  102. ...
  103. ...
  104. ComThread.Release();
  105. ...
  106. ...
  107. ComThread.quitMainSTA();
  108. </pre>
  109. </code>
  110. <br> In this case, you can also create the Main STA explicitly:
  111. <br>
  112. <code>
  113. <pre>
  114. ComThread.startMainSTA();
  115. ...
  116. ...
  117. Thread 1:
  118. ComThread.InitMTA();
  119. ...
  120. ...
  121. ComThread.Release();
  122. ...
  123. Thread 2:
  124. ComThread.InitMTA();
  125. ...
  126. ...
  127. ComThread.Release();
  128. ...
  129. ...
  130. ComThread.quitMainSTA();
  131. </pre>
  132. </code>
  133. <br> In the latter case, all Apartment threaded components will be created in JACOB's main STA. This still has all the problems
  134. of components sharing the same Main STA and creating a bottleneck. To avoid that, you can also create STA threads yourself:
  135. <br>
  136. <code>
  137. <pre>
  138. ComThread.startMainSTA();
  139. ...
  140. ...
  141. Thread 1:
  142. ComThread.InitSTA();
  143. ...
  144. ...
  145. ComThread.Release();
  146. ...
  147. Thread 2:
  148. ComThread.InitMTA();
  149. ...
  150. ...
  151. ComThread.Release();
  152. ...
  153. ...
  154. ComThread.quitMainSTA();
  155. </pre>
  156. </code>
  157. <br> In this example, thread 1 is an STA and thread 2 is an MTA. You could omit the call to ComThread.startMainSTA(), but if
  158. you do, then COM will make the first STA your main one, and then if you exit that thread, the application will crash.
  159. <p></p>
  160. Actually, Thread 1 is
  161. <i>almost</i> an STA. It's lacking a windows message loop. So, this type of STA is fine as long as you are creating a component
  162. and using it in the same thread, and not makind event callbacks.
  163. <H3>JACOB's STA Class</H3>
  164. If you want to create an true STA where you can create a component and then let other threads call methods on it, then you
  165. need a windows message loop. JACOB provides a class called:
  166. <code>com.jacob.com.STA</code> which does exactly this.
  167. <code>
  168. <pre>
  169. public class com.jacob.com.STA extends java.lang.Thread
  170. {
  171. public com.jacob.com.STA();
  172. public boolean OnInit(); // you override this
  173. public void OnQuit(); // you override this
  174. public void quit(); // you can call this from ANY thread
  175. }
  176. </pre>
  177. </code>
  178. <p></p>
  179. The STA class extends
  180. <code>java.lang.Thread</code> and it provides you with two methods that you can override:
  181. <code>OnInit</code> and
  182. <code>OnQuit</code>. These methods are called from the thread's
  183. <code>run</code> method so they will execute in the new thread. These methods allow you to create COM components (Dispatch objects) and release
  184. them. To create an STA, you subclass it and override the OnInit.
  185. <p></p>
  186. The
  187. <code>quit</code> method is the
  188. <b>only</b> other method that can be called from any thread. This method uses the Win32 function
  189. <code>PostThreadMessage</code> to force the STA's windows message loop to exit, thereby terminating the thread.
  190. <p></p>
  191. You will then need to make calls into the component that is running in the STA thread. If you simply try to make calls from
  192. another thread on a Dispatch object created in the STA thread, you will get a COM Exception. For more details see:
  193. <a href="http://www.develop.com/effectivecom">
  194. Don Box 'Effective COM' Rule 29</a>: Don't Access raw interface pointers across apartment boundaries.
  195. <H3>The DispatchProxy Class</H3>
  196. Since you cannot call methods directly on a Dispatch object created in another STA JACOB provides a method for the class
  197. that created the Dispatch object to marshal it to your thread. This is done via the
  198. <code>com.jacob.com.DispatchProxy</code> class.
  199. <code>
  200. <pre>
  201. public class DispatchProxy extends JacobObject {
  202. public DispatchProxy(Dispatch);
  203. public Dispatch toDispatch();
  204. public native void release();
  205. public void finalize();
  206. }
  207. </code>
  208. </pre>
  209. <p></p>
  210. This class works as follows: the thread that created the Dispatch object constructs an instance of DispatchProxy(Dispatch)
  211. with the Dispatch as a parameter. This instance can then be accessed from another thread, which will invoke its
  212. <code>toDispatch</code> method proxy as if it were local to your thread. COM will do the inter-thread marshalling transparently.
  213. <p></p>
  214. The following example is part of samples/test/ScriptTest2.java in the JACOB distribution. It shows how you can create the
  215. ScriptControl in one STA thread and make method calls on it from another:
  216. <code>
  217. <pre>
  218. import com.jacob.com.*;
  219. import com.jacob.activeX.*;
  220. class ScriptTest2 extends STA
  221. {
  222. public static ActiveXComponent sC;
  223. public static Dispatch sControl = null;
  224. public static DispatchProxy sCon = null;
  225. public boolean OnInit()
  226. {
  227. try
  228. {
  229. System.out.println("OnInit");
  230. System.out.println(Thread.currentThread());
  231. String lang = "VBScript";
  232. sC = new ActiveXComponent("ScriptControl");
  233. sControl = (Dispatch)sC.getObject();
  234. // sCon can be called from another thread
  235. sCon = new DispatchProxy(sControl);
  236. Dispatch.put(sControl, "Language", lang);
  237. return true;
  238. }
  239. catch (Exception e)
  240. {
  241. e.printStackTrace();
  242. return false;
  243. }
  244. }
  245. public void OnQuit()
  246. {
  247. System.out.println("OnQuit");
  248. }
  249. public static void main(String args[]) throws Exception
  250. {
  251. try {
  252. ComThread.InitSTA();
  253. ScriptTest2 script = new ScriptTest2();
  254. Thread.sleep(1000);
  255. // get a thread-local Dispatch from sCon
  256. Dispatch sc = sCon.toDispatch();
  257. // call a method on the thread-local Dispatch obtained
  258. // from the DispatchProxy. If you try to make the same
  259. // method call on the sControl object - you will get a
  260. // ComException.
  261. Variant result = Dispatch.call(sc, "Eval", args[0]);
  262. System.out.println("eval("+args[0]+") = "+ result);
  263. script.quit();
  264. System.out.println("called quit");
  265. } catch (ComException e) {
  266. e.printStackTrace();
  267. }
  268. finally
  269. {
  270. ComThread.Release();
  271. }
  272. }
  273. }
  274. </pre>
  275. </code>
  276. <p></p>
  277. You can try to modify the
  278. <code>Dispatch.call</code> invocation in the main thread to use
  279. <code>sControl</code> directly, and you will see that it fails. Notice that once we construct the ScriptTest2 object in the main thread, we sleep
  280. for a second to allow the other thread time to initialize itself.
  281. <p></p>
  282. The STA thread calls
  283. <code>sCon = new DispatchProxy(sControl);</code> to save a global reference to the DispatchProxy that represents the
  284. <code>sControl</code> object. The main thread then calls:
  285. <code>Dispatch sc = sCon.toDispatch();</code> to get a local Dispatch proxy out of the DispatchProxy object.
  286. <p></p>
  287. At most
  288. <b>one(!)</b>
  289. thread can call toDispatch(), and the call can be made only once. This is because a IStream object is used to pass the proxy,
  290. and it is only written once and closed when you read it. If you need multiple threads to access a Dispatch pointer, then
  291. create that many DispatchProxy objects. For more details please refer to the Don Box reference above.
  292. <H3>Recommended Procedure</H3>
  293. <ul>
  294. <li>
  295. It is recommended that you always allow JACOB to manage the main STA rather than letting COM create one on its own or tag
  296. one of yours.
  297. </li>
  298. <li>
  299. Declare an STA thread using ComThread.InitSTA() if all your method calls for that component are going to come from the same
  300. thread.
  301. </li>
  302. <li>
  303. If you want an STA thread that allows other threads to call into it, use the
  304. <code>com.jacob.com.STA</code> class as outlined above.
  305. </li>
  306. <li>
  307. If you have a COM component that declares its ThreadingModel as "Free" or "Both", then use the MTA.
  308. </li>
  309. <li>
  310. In most cases, if you need to make method calls from multiple threads, you can simply use MTA threads, and allow COM to create
  311. the components in the Main STA. You should only create your own STA's and DispatchProxy if you understand COM well enough
  312. to know when the MTA solution will fail or have other shortcomings.</li>
  313. </ul>
  314. There are 3 examples in the samples/test directory that demonstrate these cases:
  315. <ul>
  316. <li>ScriptTest.java - creates an STA for the ScriptControl component and runs all its method calls from that STA.</li>
  317. <li>ScriptTest2.java - creates a separate STA thread, and makes method calls into the component from another thread using DispatchProxy.
  318. </li>
  319. <li>ScriptTest3.java - creates a separate MTA thread, and makes method calls into the component from another MTA thread. This
  320. is simpler than ScriptTest2 for most applications.</li>
  321. </ul>
  322. <h3>Default Threading Model</h3>
  323. If you create a new thread, and don't call
  324. <code>ComThread.InitSTA()</code> or
  325. <code>ComThread.InitMTA()</code> on it, then the first time your java code creates a JacobObject, it will try to register itself with the ROT, and when it
  326. sees that the current thread is not initialized, it will initialize it as MTA. This means that the code to do this is no
  327. longer inside the native jni code - it is now in the
  328. <code>com.jacob.com.ROT</code> class. For more details on the ROT, see the
  329. <a href="JacobComLifetime.html">Object Lifetime</a> document.