|
This article discusses both how to start an OpenSpan adapter and multithreading. While these seem like separate issues, in fact how you start your application impacts how to work with events so it is best to discuss this in a single article.
An OpenSpan adapter is a class generated by the OpenSpan tools which encapsulates the data in an application and permits your code to monitor and modify how the application works.
Starting an OpenSpan Adapter from .Net
There are a few ways to start an OpenSpan adapter from a .Net project. The simplest is to use the adapter’s Start().
// Starts the external application Crm.Start(); }
The Start()method can also take a parameter which provides a synchronization object. This object must implement the ISynchronizeInvoke interface. In practical terms, you pass it a Winform UI object such as a Form or a control. Doing this allows the OpenSpan adapters to automatically marshal their events to your UI so you don’t have to. This is because in Windows, you cannot modify a UI element safely except from the thread the UI element was created on.
You only need to do this for adapters which you handle events from and that the event handlers modify UI items. If either of those conditions are not true, then you don’t need to pass an object to the Start() method.
// Starts the external application Crm.Start( this ); // this is a System.Windows.Forms.Form object }
Multithreading with Adapters and .Net
If you start your application adapter with Start() and no synchronization, you will then need to handle marshalling events explicitly. This has the advantage of giving your code more control over how it is done, with the disadvantage of you must remember to do this or else you will likely have exceptions thrown while your application is running.
Microsoft Windows requires that all changes made to a user interface control be done by the thread that created the control. This is simply how Win32 works. However, it is something you need to be aware of since it will happen when handling events from external application adapters.
OpenSpan adapters will fire their events on different threads than your .Net applications UI thread. This keeps your application’s UI responsive however it also means that you need to marshal events to the UI thread to work with controls like textboxes and labels. If you don’t do this, you will get cross-threading exceptions.
This is not needed if your event handler does not touch a UI control or if you do not handle events from the application.
Advantages / Disadvantages to each Method
Start( this ). The advantages to using this technique are that it is easy and safe. You won’t need to worry about when you need to marshal code. Usually you have a System.Windows.Forms.Form object that you can pass to Start() as a this object and it will then correctly marshal all events for you.
The disadvantage is that when your code is in an event handler, it will be on the thread of the UI object and the UI object will not handle any repaint or other events until your code is done. This is only an issue if your code takes a long time to complete. This is no different from the usual event handlers in .Net code. In these cases where it does take a long time, you can place calls to Update() in your code to force a repaint or call the message loop, Application.DoEvents() or you can spin off a different thread to handle the processing. Normally though this is not an issue.
One thing to note, is that all control events are marshaled for you. However, at this time, adapter events (Starting, Stopping, Stopped) are not marshaled and you still need to use Invoke() if you use these events and modify a UI while handling the event.
Start() You have more control with this method of starting an adapter, but you must use Invoke() to marshal UI references to the correct thread.
The advantage then is more control, the disadvantage is you need to be more careful when coding and debugging. Also, same problem of long running events can still happen if you marshal the code to the UI thread and then take a long time in that thread.
How to Marshal events in your code
This only applies if you use Start() without giving it an object reference to synchronize with.
The following is a sample pattern for handling an UI control safely from different threads. There are many patterns you can use; this one is self-contained. You can find out more by searching MSDN. For instance the article, http://blogs.msdn.com/csharpfaq/archive/2004/03/17/91685.aspx describes this.
The important thing is to use Invoke() if updating anything on a UI control. Some examples are changing Text, clearing a listbox, or working with a DataGridView. If you do use Invoke(), you will need to use a delegate. The example below shows a delegate for passing a single string argument.
public delegate void updateUI( string key );
/// /// Handles updating the appliation bar's values. It’s done here so we can ensure its /// thread safe for calls from the OpenSpan adapter. /// /// account number for the window we are working with public void handleToolbarUpdate( string keyText ) { if ( InvokeRequired ) { // Call self but on the correct thread Invoke( new updateUI( handleToolbarUpdate ), keyText ); return; }
… the rest of your code …
However, if you do not need to pass any arguments, then you can use the MethodInvoker delegate which is part of .Net such as:
if ( InvokeRequired ) { Invoke( new MethodInvoker( updateLabels ) ); return; }
|