title: C# COM server events “lost” when raising events while Excel is in edit mode
body: <p>I have an in-proc COM server written in C# (using .NET Framework 3.5) that raises COM events based on this example:
  <a href="http://msdn.microsoft.com/en-us/library/dd8bf0x3(v=vs.90).aspx" rel="nofollow noreferrer">http://msdn.microsoft.com/en-us/library/dd8bf0x3(v=vs.90).aspx</a></p>
  
  <p>Excel VBA is the most common client of my COM server.  I’ve found that when I raise COM events while Excel is in edit mode (e.g. a cell is being edited) the event is “lost”.  Meaning, the VBA event handler is never called (even after the Excel edit mode is finished) and the call to the C# event delegate passes through and fails silently with no exceptions being thrown.  Does anyone know how I can detect this situation on my COM server?  Or better still make the event delegate call block until Excel is out of edit mode?</p>
  
  <p>I have tried:</p>
  
  <ul>
  <li>Inspecting the properties of the event delegate – could not find any property to indicate that the event failed to be raised on the client.</li>
  <li>Calling the event delegate directly from a worker thread and from the main thread – event not raised on the client, no exceptions thrown on the server.</li>
  <li>Pushing the event delegate onto a worker thread’s <a href="http://msdn.microsoft.com/en-us/library/System.Windows.Threading.Dispatcher(v=vs.90).aspx" rel="nofollow noreferrer">Dispatcher</a> and invoking it synchronously – event not raised on the client, no exceptions thrown on the server.</li>
  <li>Pushing the event delegate onto the main thread’s Dispatcher and invoking it synchronously and asynchronously – event not raised on the client, no exceptions thrown on the server.</li>
  <li>Checking the status code of the Dispatcher.BeginInvoke call (using DispatcherOperation.Status) – the status is always ends with “Completed”, and is never in “Aborted” state.</li>
  <li>Creating an out-of-proc C# COM server exe and tested raising the events from there – same result, event handler never called, no exceptions.</li>
  </ul>
  
  <p>Since I get no indication that the event was not raised on the client, I cannot handle this situation in my code.</p>
  
  <p>Here is a simple test case.  The C# COM server:</p>
  
  <pre><code>namespace ComServerTest
  {
      public delegate void EventOneDelegate();
  
      // Interface
      [Guid("2B2C1A74-248D-48B0-ACB0-3EE94223BDD3"), Description("ManagerClass interface")]
      [InterfaceType(ComInterfaceType.InterfaceIsDual)]
      [ComVisible(true)]
      public interface IManagerClass
      {
          [DispId(1), Description("Describes MethodAAA")]
          String MethodAAA(String strValue);
  
          [DispId(2), Description("Start thread work")]
          String StartThreadWork(String strIn);
  
          [DispId(3), Description("Stop thread work")]
          String StopThreadWork(String strIn);
      }
  
      [Guid("596AEB63-33C1-4CFD-8C9F-5BEF17D4C7AC"), Description("Manager events interface")]
      [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
      [ComVisible(true)]
      public interface ManagerEvents
      {
          [DispId(1), Description("Event one")]
          void EventOne();
      }
  
      [Guid("4D0A42CB-A950-4422-A8F0-3A714EBA3EC7"), Description("ManagerClass implementation")]
      [ComVisible(true), ClassInterface(ClassInterfaceType.None)]
      [ComSourceInterfaces(typeof(ManagerEvents))]
      public class ManagerClass : IManagerClass
      {
          private event EventOneDelegate EventOne;
  
          private System.Threading.Thread m_workerThread;
          private bool m_doWork;
          private System.Windows.Threading.Dispatcher MainThreadDispatcher = null;
  
          public ManagerClass()
          {
              // Assumes this is created on the main thread
              MainThreadDispatcher = System.Windows.Threading.Dispatcher.CurrentDispatcher;
  
              m_doWork = false;
              m_workerThread = new System.Threading.Thread(DoThreadWork);
          }
  
          // Simple thread that raises an event every few seconds
          private void DoThreadWork()
          {
              DateTime dtStart = DateTime.Now;
              TimeSpan fiveSecs = new TimeSpan(0, 0, 5);
              while (m_doWork)
              {
                  if ((DateTime.Now - dtStart) &gt; fiveSecs)
                  {
                      System.Diagnostics.Debug.Print("Raising event...");
                      try
                      {
                          if (EventOne != null)
                          {
                              // Tried calling the event delegate directly
                              EventOne();
  
                              // Tried synchronously invoking the event delegate from the main thread's dispatcher
                              MainThreadDispatcher.Invoke(EventOne, new object[] { });
  
                              // Tried asynchronously invoking the event delegate from the main thread's dispatcher
                              System.Windows.Threading.DispatcherOperation dispOp = MainThreadDispatcher.BeginInvoke(EventOne, new object[] { });
  
                              // Tried synchronously invoking the event delegate from the worker thread's dispatcher.
                              // Asynchronously invoking the event delegate from the worker thread's dispatcher did not work regardless of whether Excel is in edit mode or not.
                              System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke(EventOne, new object[] { });
                          }
                      }
                      catch (System.Exception ex)
                      {
                          // No exceptions were thrown when attempting to raise the event when Excel is in edit mode
                          System.Diagnostics.Debug.Print(ex.ToString());
                      }
  
                      dtStart = DateTime.Now;
                  }
              }
          }
  
          // Method should be called from the main thread
          [ComVisible(true), Description("Implements MethodAAA")]
          public String MethodAAA(String strValue)
          {
              if (EventOne != null)
              {
                  try
                  {
                      // Tried calling the event delegate directly
                      EventOne();
  
                      // Tried asynchronously invoking the event delegate from the main thread's dispatcher
                      System.Windows.Threading.DispatcherOperation dispOp = System.Windows.Threading.Dispatcher.CurrentDispatcher.BeginInvoke(EventOne, new object[] { });
  
                      // Tried synchronously invoking the event delegate from the main thread's dispatcher
                      System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke(EventOne, new object[] { });
                  }
                  catch (System.Exception ex)
                  {
                      // No exceptions were thrown when attempting to raise the event when Excel is in edit mode
                      System.Diagnostics.Debug.Print(ex.ToString());
                  }
                  return "";
              }
  
              return "";
          }
  
          [ComVisible(true), Description("Start thread work")]
          public String StartThreadWork(String strIn)
          {
              m_doWork = true;
              m_workerThread.Start();
              return "";
          }
  
          [ComVisible(true), Description("Stop thread work")]
          public String StopThreadWork(String strIn)
          {
              m_doWork = false;
              m_workerThread.Join();
              return "";
          }
      }
  }
  </code></pre>
  
  <p>I register it using regasm:</p>
  
  <pre><code>%SystemRoot%\Microsoft.NET\Framework\v2.0.50727\regasm /codebase ComServerTest.dll /tlb:ComServerTest.tlb
  </code></pre>
  
  <p>Excel VBA client code:</p>
  
  <pre><code>Public WithEvents managerObj As ComServerTest.ManagerClass
  Public g_nCounter As Long
  
  Sub TestEventsFromWorkerThread()
      Set managerObj = New ComServerTest.ManagerClass
  
      Dim dtStart As Date
      dtStart = DateTime.Now
  
      g_nCounter = 0
  
      Debug.Print "Start"
  
      ' Starts the worker thread which will raise the EventOne event every few seconds
      managerObj.StartThreadWork ""
  
      Do While True
          DoEvents
  
          ' Loop for 20 secs
          If ((DateTime.Now - dtStart) * 24 * 60 * 60) &gt; 20 Then
              ' Stops the worker thread
              managerObj.StopThreadWork ""
              Exit Do
          End If
  
      Loop
  
      Debug.Print "Done"
  
  End Sub
  
  Sub TestEventFromMainThread()
      Set managerObj = New ComServerTest.ManagerClass
  
      Debug.Print "Start"
  
      ' This call will raise the EventOne event
      managerObj.MethodAAA ""
  
      Debug.Print "Done"
  End Sub
  
  ' EventOne handler
  Private Sub managerObj_EventOne()
      Debug.Print "EventOne " &amp; g_nCounter
      g_nCounter = g_nCounter + 1
  End Sub
  </code></pre>
  
  <p>Edit 27/11/2014 - I've been doing some more investigation on this.</p>
  
  <p>This problem <em>also</em> occurs for a C++ MFC Automation server that raises COM events.  If I raise the COM event from the main thread when Excel is in edit mode, the event handler is never called.  No errors or exceptions are thrown on the server, similar to my C# COM server.  <strong>However</strong>, if I use the Global Interface Table to marshal the event sink interface from the main thread <em>back</em> to the main thread, then invoking the event - it will block while Excel is in edit mode.  (I also used COleMessageFilter to disable the busy dialog and not responding dialogs, otherwise I'd receive the exception: <em>RPC_E_CANTCALLOUT_INEXTERNALCALL It is illegal to call out while inside message filter.</em>)</p>
  
  <p>(let me know if you'd like to see the MFC Automation code, I'm skipping it for brevity)</p>
  
  <p>Knowing that, I tried to do the same on my C# COM server.  I could instantiate the Global Interface Table (using the definition from pinvoke.net) and the message filter (using the IOleMessageFilter definition from MSDN).  However, the event still gets "lost" and does not block while Excel is in edit mode.</p>
  
  <p>Here's how I modified my C# COM server:</p>
  
  <pre><code>namespace ComServerTest
  {
      // Global Interface Table definition from pinvoke.net
      [
          ComImport,
          InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
          Guid("00000146-0000-0000-C000-000000000046")
      ]
      interface IGlobalInterfaceTable
      {
          uint RegisterInterfaceInGlobal(
                  [MarshalAs(UnmanagedType.IUnknown)] object pUnk,
                  [In] ref Guid riid);
  
          void RevokeInterfaceFromGlobal(uint dwCookie);
  
          [return: MarshalAs(UnmanagedType.IUnknown)]
          object GetInterfaceFromGlobal(uint dwCookie, [In] ref Guid riid);
      }
  
      [
          ComImport,
          Guid("00000323-0000-0000-C000-000000000046") // CLSID_StdGlobalInterfaceTable
      ]
      class StdGlobalInterfaceTable /* : IGlobalInterfaceTable */
      {
      }
  
      public class ManagerClass : IManagerClass
      {
          //...skipped code already mentioned in earlier sample above...
          //...also skipped the message filter code for brevity...
          private Guid IID_IDispatch = new Guid("00020400-0000-0000-C000-000000000046");
          private IGlobalInterfaceTable m_GIT = null;
  
          public ManagerClass()
          {
              //...skipped code already mentioned in earlier sample above...
              m_GIT = (IGlobalInterfaceTable)new StdGlobalInterfaceTable();
          }
  
          public void FireEventOne()
          {
              // Using the GIT to marshal the (event?) interface from the main thread back to the main thread (like the MFC Automation server).
              // Should we be marshalling the ManagerEvents interface pointer instead?  How do we get at it?
              uint uCookie = m_GIT.RegisterInterfaceInGlobal(this, ref IID_IDispatch);
              ManagerClass mgr = (ManagerClass)m_GIT.GetInterfaceFromGlobal(uCookie, ref IID_IDispatch);
              mgr.EventOne(); // when Excel is in edit mode, event handler is never called and does not block, event is "lost"
              m_GIT.RevokeInterfaceFromGlobal(uCookie);
          }
      }
  }
  </code></pre>
  
  <p>I’d like my C# COM server to behave in a similar way to the MFC Automation server.  Is this possible?  I think I should be registering the ManagerEvents interface pointer in the GIT but I don't know how to get at it?  I tried using Marshal.GetComInterfaceForObject(this, typeof(ManagerEvents)) but that just throws an exception: <em>System.InvalidCastException: Specified cast is not valid.</em></p>