如何处理来自控制台应用程序的 COM 事件? [英] How to handle COM events from a console application?

查看:27
本文介绍了如何处理来自控制台应用程序的 COM 事件?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用来自第三方库的 COM 对象,该对象生成周期性事件.当我使用 Winforms 应用程序中的库,将该对象作为类成员并在主窗体线程中创建它时,一切正常.但是,如果我从另一个线程创建对象,则不会收到任何事件.

我的猜测是我需要在用于创建对象的同一线程中有某种事件循环.

我需要从控制台应用程序使用这个对象.我想我可以使用 Application.DoEvents,但我宁愿不在控制台应用程序中包含 Winforms 命名空间.

我该如何解决这个问题?

更新 3 (2011-06-15): 供应商终于给出了答复.简而言之,他们说 Application.Run 创建的消息泵和 Thread.Join 创建的消息泵有一些区别,但他们不知道这种区别是什么.

我同意他们的看法;对此问题的任何了解将不胜感激.

更新:

从理查德评论到 mdm 回答:

如果有其他组件是单线程的并从 MTA 实例化,则 Windows 将创建工作线程 + 窗口 + 消息泵并进行必要的编组.

为了听从他的建议,我正在做以下事情:

更新 2:

我根据 João Angelo 的回答更改了代码.

使用系统;命名空间 ConsoleApplication2{课程计划{[STAThread]静态无效主(字符串 [] args){MyComObjectWrapper 包装器 = new MyComObjectWrapper();}}类 MyComObjectWrapper{MyComObject m_Object;AutoResetEvent m_Event;公共 MyComObjectWrapper(){m_Event = new System.Threading.AutoResetEvent(false);System.Threading.Thread t = new System.Threading.Thread(() => CreateObject());t.SetApartmentState (System.Threading.ApartmentState.STA);t.开始();等待();}void ObjectEvt(/*...*/){//...}空等待(){m_Event.WaitOne();}无效的创建对象(){m_Object = new MyComObject();m_Object.OnEvent += ObjectEvt;System.Threading.Thread.CurrentThread.Join();}}}

我也尝试了以下方法:

 public MyComObjectWrapper(){创建对象();}

解决方案

如其他答案中所述,STA COM 组件需要运行消息循环,以便将其他线程中发生的调用正确编组到拥有的 STA 线程组件.

在 Windows 窗体中,您可以免费获得消息循环,但在控制台应用程序中,您必须通过在拥有 COM 组件的线程上调用 Thread.CurrentThread.Join 来显式执行此操作,这可能是也是应用程序的主线程.该线程必须是STA.

来自 Thread.Join 你可以看到这是你想要的:

<块引用>

阻塞调用线程直到线程终止,同时继续执行标准 COM 和 SendMessage 泵送.

如果您不想在主控制台线程中做任何其他事情,您只需无限期地等待,否则您可以在定期调用 Thread.CurrentThread.Join 以抽取消息的同时做其他事情.>

旁注:这里假设您正在处理 STA COM 组件.


一个简化的例子:

class 程序{[STAThread]静态无效主(字符串 [] args){var myComObj = new MyComObject();myComObj.OnEvent += ObjectEvt;Thread.CurrentThread.Join();//永远等待}static void ObjectEvt(object sender, EventArgs e) { }}

在这个例子中,控制台应用程序将处于一个永无止境的循环中,除了响应来自 COM 组件的事件之外,什么都不做.如果这不起作用,您应该尝试从 COM 组件供应商那里获得支持.

I'm using a COM object from a third party library that generates periodic events. When I use the library from a Winforms app, having the object as a class member and creating it in the main form thread, everything works. However, if I create the object from another thread, I don't receive any event.

My guess is that I need to have some kind of event loop in the same thread used to create the object.

I need to use this object from a console application. I guess I could use Application.DoEvents, but I'd rather not include the Winforms namespace in a console App.

How can I solve this problem?

Update 3 (2011-06-15): The vendor has answered at last. In short, they say there is some difference between the message pump created by Application.Run and the one created by Thread.Join, but they don't know what that difference is.

I agree with them; any light shed on this matter would be very appreciated.

Update:

From Richard comment to mdm answer:

if there other component is single threaded and instantiated from an MTA then Windows will create the worker thread + window + message pump and do the necessary marshalling.

Trying to follow his advice, I'm doing the following:

Update 2:

I'm changed the code following João Angelo 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 have also tried the following instead:

        public MyComObjectWrapper()
        {
            CreateObject();
        }

解决方案

As already stated in other answers STA COM components require a message loop to be run in order for calls happening in other threads be correctly marshaled to the STA thread that owns the component.

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

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

Blocks the calling thread until a thread terminates, while continuing to perform standard COM and SendMessage pumping.

If you don't want to do anything else in the main console thread you just wait indefinitely, otherwise you can do other stuff while periodically calling Thread.CurrentThread.Join to pump messages.

Side-note: This assumes you're dealing with a 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 a never ending loop that should do nothing more then respond to events from the COM component. If this does not work you should try to get support from the COM component vendor.

这篇关于如何处理来自控制台应用程序的 COM 事件?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆