Silverlight 4 COM Interop – Raising Events on the correct thread

If Silverlight is crashing from asynchronous events raised in the COM server, chances are your background worker isn’t raising events on the right thread.

The debugging main trick is to look at what threads are being called from the background events. (Menu Debug > Windows > Threads).

I noticed that when a backgroundworker finishes, it is not on the main thread (probably because the COM server doesn’t have any windows?).

So the next step is to create a dummy control on the same thread called by Silverlight, and then call dummy.Invoke use SynchronizationContext.Current.Post so that the event is raised on the same thread again.

Some code (COM Server – call “gacutil /i ClassLibrary1.dll” and “regasm ClassLibrary.dll” before use):

namespace ClassLibrary1
{

using System.Runtime.InteropServices;
using System.ComponentModel;

public delegate void ClickDelegate(int x, int y);

// Step 1: Defines an event sink interface (ButtonEvents) to be
// implemented by the COM sink.
[GuidAttribute("1A585C4D-3371-48dc-AF8A-AFFECC1B0967")]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)]
[ComVisible(true)]
public interface ButtonEvents
{
[DispId(1)]
void Click(int x, int y);
}

// Step 2: Connects the event sink interface to a class
// by passing the namespace and event sink interface
// ("EventSource.ButtonEvents, EventSrc").
[ComSourceInterfaces(typeof(ButtonEvents))]
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.AutoDispatch)]
public class Button
{
public event ClickDelegate Click;
//private System.Windows.Forms.Control _dummy;

BackgroundWorker w = new BackgroundWorker();

public Button()
{
w.DoWork += new DoWorkEventHandler(w_DoWork);
w.RunWorkerCompleted += new RunWorkerCompletedEventHandler(w_RunWorkerCompleted);
}

void w_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
  // _dummy.Invoke(Click, new object[] { 150, 151 });
  System.Threading.SynchronizationContext.Current.Post(
    new System.Threading.SendOrPostCallback(
      (o)=> Click(180, 181)), null);
}

void w_DoWork(object sender, DoWorkEventArgs e)
{
System.Threading.Thread.Sleep(3000);
}

public void CauseClickEvent(int x, int y)
{
//_dummy = new System.Windows.Forms.Control();
//_dummy.CreateControl();
w.RunWorkerAsync();
}

}

}

Silverlight code:

using System;
using System.Windows;
using System.Windows.Controls;
using System.Runtime.InteropServices.Automation;

namespace SilverlightComAutomation
{
public partial class MainPage : UserControl
{

dynamic comButton;

public MainPage()
{
InitializeComponent();
comButton = AutomationFactory.CreateObject("ClassLibrary1.Button");
AutomationEvent event1 = AutomationFactory.GetEvent(comButton, "Click");
event1.EventRaised += new EventHandler(event1_EventRaised);
}

private void button1_Click(object sender, RoutedEventArgs e)
{

comButton.CauseClickEvent(5, 20);

}

void event1_EventRaised(object sender, AutomationEventArgs e)
{
MessageBox.Show(String.Format("Click from COM {0},{1}", e.Arguments[0], e.Arguments[1]));
}
}
}

About this entry