7

我有一个外部组件 (C++),我想从我的 C# 代码中调用它。

代码是这样的:

using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace dgTEST
{
    class Program
    {
        [STAThread]
        static void Main(string[] args)
        {
            ExtComponentCaller extCompCaller = new ExtComponentCaller();
            result = extCompCaller.Call(input);

            Thread t = new Thread(new ThreadStart(() =>
            {
                try
                {
                    result = extCompCaller.Call(input);
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.ToString());
                }
            }));

            t.SetApartmentState(ApartmentState.STA);
            t.Start();
            t.Join();
        }
    }
}

所以问题是,在第一次调用时它运行良好,调用了外部组件,我得到了结果。

但是当我尝试在另一个线程中调用它时,我遇到了一个异常: System.InvalidCastException: Unable to cast COM object of type 'System.__ComObject' ... 。由于 STAThread,我确定抛出了这个异常。因为如果我从 Main 函数中删除 [STAThread] 属性,外部组件的第一次调用也会发生同样的情况,它工作正常。

如何从其他线程调用此外部组件以消除此异常?

更新 - - - - - - -

现在发生了其他疯狂的事情。当我使用 F5 从 Visual Studio 启动程序时,问题也出现在第一次调用中,但是当我直接执行二进制 .exe 文件时,它正在工作(从另一个线程它不是:()。如果我切换从 Debug 到 Release 的构建并使用 F5 从 Visual Studio 启动它,第一个调用再次工作。

为什么会这样?

提前感谢您的帮助!

最好的问候,佐利

4

1 回答 1

2

线程从来都不是一个小细节。如果代码没有明确记录支持线程,那么 99% 的可能性是它不支持它。

显然这个组件不支持线程。创建另一个 STA 线程不是神奇的解决方案,它仍然是一个不同的线程。InvalidCastException 告诉您它还缺少编组来自工作线程的调用所需的代理/存根支持,就像您尝试创建的那样。需要对非线程安全的代码进行线程安全调用。尽管您确实违反了 [STAThread] 的合同,但它必须泵送一个消息循环。消息循环允许从工作线程调用非线程安全的组件。你从 Application.Run() 中得到一个消息循环。

这是降压停止的地方。它不是线程安全的,期间。即使修复了您的主线程或要求供应商或作者为您提供代理/存根,您仍然没有完成您打算做的事情,它实际上不会在您创建的那个工作线程上运行。所以它必须看起来像这样:

    static void Main(string[] args)
    {
        Thread t = new Thread(new ThreadStart(() =>
        {
             ExtComponentCaller extCompCaller = new ExtComponentCaller();
             result = extCompCaller.Call(input);
        }));

        t.SetApartmentState(ApartmentState.STA);
        t.Start();
        t.Join();
    }

它在您进行调用的同一线程上创建对象,因此它是线程安全的。仍然存在这个工作线程不泵送消息循环的问题,COM 组件往往依赖于此。您会从死锁或未运行的事件中发现这是否是一个问题。如果当您从主线程调用它时它已经在您的测试程序中运行良好,那么您可能可以不用抽水。

于 2013-04-19T13:39:52.190 回答