【问题标题】:C# COM Objects and MultithreadingC# COM 对象和多线程
【发布时间】:2014-12-27 12:28:15
【问题描述】:

我目前正在开发一个与串行设备通信的 Windows 窗体应用程序。设备供应商提供了一个包含交互方法的 *.dll 文件。我在 Visual Studio 中添加了对 *.dll 文件的引用。
如果我调用设备库 (Get()) 的函数,我会在 2 秒后得到响应。为了避免冻结我的 GUI,我生成了一个新线程,它初始化库对象的一个​​新实例并调用 Get() 方法。
但是,调用 Get() 会冻结我的 GUI 正好 2 秒。似乎该对象已经在主线程中初始化。
我不知道我在代码中遗漏了什么。这是重现我的问题的代码的 sn-p:

    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            MyDevice deviceObj = new MyDevice();
            Thread myThread = new Thread(new ThreadStart(deviceObj.getValues));

            myThread.IsBackground = true;
            myThread.SetApartmentState(ApartmentState.STA);
            myThread.Start();
        }
    }

    class MyDevice
    {
        public void getValues()
        {

            // initialize object of device library
            Tcddka.tcddk tcd = new Tcddka.tcddk();

            // (comPort, identifier, timeout)
            tcd.Init((Int16)(3 - 1), "deviceID", 7000);

            for (int i = 0; i < 10; i++)
            {
                tcd.Get(); // measure new values
                Thread.Sleep(2000);
            }
        }
    }



提前感谢您的努力,
迈克尔

编辑:解决方案

  1. 实现STAThread,派生出你自己的类。覆盖 Initialize()(不要忘记调用 base.Initialize() 并在此处创建您的 COM 对象)
  2. 我的 DLL 库未注册。打开命令行,输入 regsvr32 "你的 DLL 文件的路径"
  3. 打开注册表,搜索您的 DLL 文件名,浏览到文件夹 InprocServer32 并检查 ThreadingModel 是否设置为 Apartment。

谢谢你们!!

【问题讨论】:

  • 您在错误的线程上创建了 MyDevice 对象。 UI 线程而不是工作线程。需要在 STA 线程中调用 Application.Run()。

标签: c# multithreading visual-studio com


【解决方案1】:

我会先检查COM组件是否有threading model set in the registry。如果未设置 ThreadingModel,则始终在 first STA 线程中创建组件。在这种情况下,您应该就这个问题联系组件作者。当然,您可以自己将 ThreadingModel 设置为“Apartment”,但我只会在等待组件作者修复注册时将其用作临时修复。

您仍然必须按照 Hans Passant 的建议在另一个 STA 线程中创建组件。

【讨论】:

  • 我检查了注册表,没有设置 ThreadingModel。我已经联系了作者,但还没有收到回复。为了解决这个问题,我尝试使用 STAThread 这个类。我派生了自己的类并覆盖了 Initialize 函数。没有成功,我的主线程仍然冻结
  • 您必须在注册表中将组件的 ThreadingModel 设置为 Apartment。否则,无论您的操作如何,组件都将在第一个 STA 线程中创建。设置注册表项后,您的代码应该可以工作(假设您记得从覆盖的 Initialize(...) 方法调用 base.Initialize(...))。
【解决方案2】:

ApartmentState.STA 表示:

线程将创建并进入一个单线程单元。

STA有一些requirements:

每个单线程单元必须有一个消息循环来处理来自同一进程内其他进程和单元的调用。没有对象的单线程公寓(仅限客户端)也需要一个消息循环来分派某些应用程序使用的广播消息。

您的主线程和您的 STA 线程正在通过 Windows 消息相互同步。如果您冻结您的 STA 线程,您最终可能会冻结您的主线程(主线程正在等待来自发送给 STA 的消息的响应,并且 STA 不为其消息循环提供服务,从而冻结您的 GUI)。反之亦然。我不知道为什么你的 COM 对象与主线程同步,这是只有你才能回答的问题。

如果可能,您应该尝试使用MTA thread。并非所有 COM 组件都可以在 MTA 中工作。

【讨论】:

  • 使用 MTA 线程不起作用。你知道如何添加一个简单的消息循环吗?
  • 如果您在睡眠(或 tcd.Get)中冻结而没有为消息循环提供服务,则消息循环无济于事。尝试找出为什么主线程和后台STA线程需要同步。在这两秒钟内中断调试器,分析堆栈。
猜你喜欢
  • 1970-01-01
  • 2011-07-19
  • 2010-09-15
  • 1970-01-01
  • 2014-09-01
  • 2014-10-30
  • 1970-01-01
  • 2012-12-15
  • 1970-01-01
相关资源
最近更新 更多