Wednesday, June 24, 2009

Accommodating an ActiveX Control that's Interfering with Windows Forms Events

For reasons not entirely clear to me a particular ActiveX control appears to interfere with Windows Forms events. The vendor has long since offered a purely managed implementation so they have little interest in supporting use of the ActiveX control via Runtime Callable Wrapper. Unfortunately, times being what they are, funds aren't available to take advantage of the purely managed implementation.

By interfere I mean, while this ActiveX control is performing a potentially long running operation the UI owning thread "magically" resumes event processing. I suspect this is intentional; the vendors users probably complained that their UIs froze whenever they performed this potentially long running operation. Since this 3rd party control was consumed mostly by single threaded VB6 apps the vendor may have decided to respond to these complaints in a way that causes a problem for Windows Forms apps.

I have lots of theories (e.g., there may be some interplay between the ActiveX control being free threaded while the main thread of the WinForms app runs in a single threaded apartment) but little time to investigate them. So I'm avoiding the problem by running the ActiveX control on a separate thread.

The managed ThreadPool is a wonderful place to run this code; I don't have to manage the setup or teardown of ThreadPool threads and the operation isn't so long running that it will prevent other threads from accessing the ThreadPool.

Since I'm running the ActiveX control in a separate thread to avoid interference with the UI thread, not because I want to execute concurrently, I need to wait until it's done before proceeding (and I *want* the UI to be unresponsive during this time - even have the WaitCursor showing). So a ManualResetEvent is signaled once the operation is complete.

What does this have to do with Pre 3.0 C#? Well, C# 2.0 introduced anonymous methods. These provide a way to succinctly pass short behavior (no more than a few operations) without having to declare a separate method.

So something along the lines of the following:

ThreadPool.QueueUserWorkItem(new WaitCallback(
delegate(object throwAway)
{
// do something here
myResetEvent.Set();
}));


followed by

myResetEvent.WaitOne();


does the trick!

No comments :

Post a Comment