我有一个外部组件(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:无法转换类型为'System .__ ComObject'...的COM对象。
我确信由于STAThread而引发了此异常。因为如果我从Main函数中删除了[STAThread]属性,则在第一次调用外部组件时也会发生同样的情况,效果很好。

如何从另一个线程调用此外部组件以摆脱此异常?

更新 - - - - - - -

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

为什么会发生?

谢谢您的帮助!

最好的祝福,
佐利

最佳答案

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

显然,该组件不支持线程。创建另一个STA线程不是魔术解决方案,它仍然是另一个线程。 InvalidCastException告诉您它也缺少编码(marshal)来自工作线程的调用所需的代理/ stub 支持,就像您尝试创建的那样。对非线程安全的代码进行线程安全调用时需要。尽管您确实违反了[STAThread]的约定,但它必须发出消息循环。它是消息循环,它允许从工作线程到不安全线程的组件进行调用。您会从Application.Run()获得消息循环。

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

    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组件往往依赖于此。您会从死锁或未运行的事件中找出问题是否存在。如果从主线程调用测试程序时它在测试程序中已经可以正常工作,则可能不需要泵浦就可以了。

关于c# - C#STAThread COMException,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/16100941/

10-10 00:58