我有一个外部组件(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]属性,那么第一次调用外部组件时也会发生同样的情况,这很正常。
如何从其他线程调用此外部组件以摆脱此异常?
UPDATE -------------
现在发生了其他疯狂的事情。当我从带有F5的Visual Studio启动程序时,问题也出现在第一次调用中,但是当我直接执行二进制.exe文件时,它正在工作(从另一个线程它不是:()。 如果我将构建从Debug切换到Release并从Visual Studio使用F5启动它,则第一次调用再次工作。
为什么会这样?
先谢谢你的帮助!
最诚挚的问候, 佐利
答案 0 :(得分: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组件倾向于依赖它。您将从死锁或未运行的事件中找出是否存在问题。如果你在测试程序中从主线程中调用它时已经可以正常工作那么你可能没有抽水了。