How to handle COM events from a console application?

I am using a COM object from a third-party library that generates periodic events. When I use the library from the Winforms application, having the object as a member of the class and creating it in the main form stream, everything works. However, if I create an object from another thread, I do not receive any event.

I assume that I need some kind of event loop in the same thread that was used to create the object.

I need to use this object from a console application. I suppose I could use Application.DoEvents, but I would prefer not to include the Winforms namespace in the console application.

How can I solve this problem?

Update 3 (2011-06-15): The supplier finally responded. In short, they say that there is some difference between the message pump created by Application.Run and the one created by Thread.Join, but they do not know what the difference is.

I agree with them; any light shed on this question will be greatly appreciated.

Update:

From Richard's comment to mdm's answer:

if the other component is single-threaded and created from MTA, then Windows will create a workflow + window + message pump and perform the necessary sorting.

Trying to follow his advice, I do the following:

Update 2:

I changed the code after Joao Angelo's answer.

using System; namespace ConsoleApplication2 { class Program { [STAThread] static void Main(string[] args) { MyComObjectWrapper wrapper = new MyComObjectWrapper(); } } class MyComObjectWrapper { MyComObject m_Object; AutoResetEvent m_Event; public MyComObjectWrapper() { m_Event = new System.Threading.AutoResetEvent(false); System.Threading.Thread t = new System.Threading.Thread(() => CreateObject()); t.SetApartmentState (System.Threading.ApartmentState.STA); t.Start(); Wait(); } void ObjectEvt(/*...*/) { // ... } void Wait() { m_Event.WaitOne(); } void CreateObject() { m_Object = new MyComObject(); m_Object.OnEvent += ObjectEvt; System.Threading.Thread.CurrentThread.Join(); } } } 

I also tried the following:

  public MyComObjectWrapper() { CreateObject(); } 
+12
c # events interop com
May 31 '11 at 9:32 a.m.
source share
6 answers

As already indicated in other answers, the STA COM components require that the message loop be started so that calls that occur in other threads are correctly distributed across the STA thread to which this component belongs.

On Windows Forms, you get a free message loop, but in a console application you must do this explicitly by calling Thread.CurrentThread.Join on a thread that owns the COM component, and is probably also the main thread for the application. This stream must be STA.

From the MSDN Thread.Join you can see that this is what you want:

Blocks the calling thread until the thread terminates, continuing to make standard COM and SendMessage calls.

If you don’t want to do anything in the main thread of the console, you just wait forever, otherwise you can do other things by periodically clicking Thread.CurrentThread.Join to send messages.

Side note: it is assumed that you are dealing with an STA COM component.




A simplified example:

 class Program { [STAThread] static void Main(string[] args) { var myComObj = new MyComObject(); myComObj.OnEvent += ObjectEvt; Thread.CurrentThread.Join(); // Waits forever } static void ObjectEvt(object sender, EventArgs e) { } } 

In this example, the console application will be in an endless loop, which should no longer respond to events from the COM component. If this does not work, you should try to get support from the COM component provider.

+3
Jun 01 '11 at 9:18
source share

If you are using STA, you will need a message loop anyway. If you don’t need a message loop, MTA is probably the easiest way, and it is also suitable for a console-style application.

One thing you need to know about is that using MTA it doesn't matter which thread created the object; all objects created by the MTA stream apply equally to all MTA threads. (Or, they say in COM, the process has exactly one multi-threaded apartment in which all MTA threads live.) This means that if you take the MTA approach, there is no need to create a separate thread at all - just create an object from the main thread. But you also need to know that incoming events will be transmitted on a "random" stream, so you will have to take separate steps to communicate with the main stream.

 using System; using System.Threading; class Program { static MyComObject m_Object; static AutoResetEvent m_Event; [MTAThread] static void Main(string[] args) { m_Event = new AutoResetEvent(false); m_Object = new MyComObject(); m_Object.OnEvent += ObjectEvt; Console.WriteLine("Main thread waiting..."); m_Event.WaitOne(); Console.WriteLine("Main thread got event, exiting."); // This exits after just one event; add loop or other logic to exit properly when appropriate. } void ObjectEvt(/*...*/) { Console.WriteLine("Received event, doing work..."); // ... note that this could be on any random COM thread. Console.WriteLine("Done work, signalling event to notify main thread..."); m_Event.Set(); } } 

A few comments on the previous version of the code that you had: you had Wait () calls in both CreateObject and the MycomObjectWrapper constructor; It seems that you should have only one - if you have two of them, only one of them will be released when you call m_Event.Set (), and the other will still wait. Also, suggest adding some debugging code to let you know how far you get. That way, you can at least say whether you receive the event from COM and separately, whether you successfully pass it back to the main thread. If the objects are marked as neutral or both in the registry, then there should be no problems with their creation from the MTA.

+5
Jun 03 2018-11-11T00:
source share

IIRC, COM events require the event loop to work, something that pumps messages and calls the Win32 GetMessage function.

Winforms does this for you, or you can emulate it using Win32 calls. This question / answer has a good example on which you can build .

+3
May 31 '11 at 9:49
source share

I think the following should work:

 [STAThread] Main(...) { var comObject = new YourComObject(); comObject.Event += EventHandler; Console.WriteLine("Press enter to exit."); Console.ReadLine(); } void EventHandler(...) { // Handle the event } 
+1
May 31 '11 at 9:44
source share

Have you identified the thread flat model?

  [STAThread] static void Main(string[] args) { // Create the thread that will manage the COM component Thread th = new Thread(...); // Before starting the thread th.SetApartmentState (ApartmentState.STA); } 

In the stream, just wait until the event reports its completion. While the thread is waiting for the event, I think that it should process messages in the thread loop.

+1
May 31 '11 at 9:50
source share

Could you try:

 static class Program { MyComObject m_Object; [STAThread] static void Main() { m_Object = new MyComObject(); m_Object.OnEvent += ObjectEvt; System.Windows.Forms.Application.Run(); } void ObjectEvt(/*...*/) { // ... } } 
+1
Jun 01 2018-11-11T00:
source share



All Articles