Thread Support
SWTtoCOM only supports single-thread apartment (STA)
Since SWTtoCOM only provides a very thin wrapper layer on top of SWT, the supported threading model must be identical to that of SWT. In SWT the application program runs the event loop in its main thread and dispatches events directly from this thread. This thread is most commonly referred to as the UI thread. All other widgets including COM components must be created in the UI thread.
Since all event code is triggered from the application's UI thread, application code that handles events, including COM component event listeners, can freely access the widgets and make graphics calls without any special techniques. However, the application is responsible for forking computational threads when performing long operations in response to an event. These forked threads must not access COM components directly and must instead use the special techniques outlined below.
Note: SWT will trigger an SWTException for any calls made from a non-UI thread that must be made from the UI thread.
SWT provides special access methods for calling widget and graphics code from a background thread.
Executing code from a non-UI thread
Applications that wish to call COM code from a non-UI thread must provide a Runnable that calls the COM code. The methods syncExec(Runnable) and asyncExec(Runnable) in the Display class are used to execute these runnables in the UI thread during the event loop.
- syncExec(Runnable) should be used when the application code in the non-UI thread depends on the return value from the COM code or otherwise needs to ensure that the runnable is run to completion before returning to the thread. SWT will block the calling thread until the runnable has been run from the application's UI thread. For example, a background thread that is computing something based on COM components current state would want to synchronously run the code to get the component state and then continue with its computations.
- asyncExec(Runnable) should be used when the application needs to perform some UI operations, but is not dependent upon the operations being completed before continuing. For example, a background thread that updates a COM component state could request the update asynchronously and continue with its processing. In this case, there is no guaranteed relationship between the timing of the ackground thread and the execution of the runnable.
The following code snippet demonstrates the pattern for using these methods:
// do time-intensive computations ... // now update the COM Component. We don't depend on the result, // so use async. display.asyncExec (new Runnable () { public void run () { if (!myCOMComponent.isDisposed()) myCOMComponent.setValue(20); } }); // now do more computations ...
It is good practice to check if your component is disposed from within the runnable
when using asyncExec. Since other things can happen in the UI thread between
the call to asyncExec
and the execution of your runnable,
you can never be sure what state your components are in by the time your runnable
executes.
Perform COM operations in a non-UI thread
The above described pattern is the one that is recommended when performing a long running operation, however if you have a long running self contained COM operation then it is possible, and probably more convenient, to reverse the above pattern.
The operation must be fully self contained which means that it must be able to
create the COM component, perform the operation and then destroy the component
without having to interact with the user. In this technique the non-ui thread creates
the COM component and uses the runnable to update the UI. When using this technique
your thread must call the OleInitialize
and OleUninitialize
methods.
As usual this is best explained with an example. Consider a component that performs a long running code generation process. The component provides an event that clients can use to update the display with a progress indicator. In this example we use a non-ui thread to perform the long running operation including interacting with the COM component and we use the above technique to update the UI.
// The run method of a non-ui thread is used to perform // the long running operation. public void run() { ICodeGen generator; try { // Call OleInitialize for this thread. OS.OleInitialize(0); generator = MyCOMComponent.create(COM.CLSCTX_INPROC_SERVER|COM.CLSCTX_LOCAL_SERVER); } catch (Exception e) { // Error handling } generator.addProgressListener(new IProgressListener() { // This method will be called by the non-ui thread so // we must use the callback method to update the ui. public void updateProgress(int worked) { display.asyncExec (new Runnable () { public void run () { if (!progressControl.isDisposed()) progressControl.setValue(20); } }); } } }); generator.generate(...); // Dispose of the object since the SWTtoCOM resource Manager will // do this in the UI thread which will cause an exception. generator.dispose(); // Call OleUninitialize for this thread. OS.OleUninitialize(); }
NOTE: You must dispose of the COM component in the same thread that created it. Failing to do so will cause the component to never get disposed of resulting in a memory leak.