我收到以下错误:
"COM object that has been separated from its underlying RCW cannot be used."
我确定问题是因为COM对象不是在它创建的线程上调用的 - STA。我试图实现IDisposable,但它对我没用。
有几个帖子处理类似的问题,但仍然无法解决我的问题:
Is it safe to call an RCW from a finalizer? Release Excel Object In My Destructor
任何人都可以发布一个示例/解释如何从另一个线程正确访问COM对象吗?
以下是显示问题的最小代码:
using System;
using System.Threading;
namespace Test.ComInterop
{
public class Program
{
MyCom _myCom;
[STAThread]
static void Main( string[] args )
{
new Program();
}
public Program()
{
_myCom = new MyCom();
// this method call works
string version = _myCom.ComMethod();
StartThread();
}
private void StartThread()
{
Thread t = new Thread( UIRun );
t.SetApartmentState( ApartmentState.STA );
t.Start();
}
void UIRun()
{
TestUI window = new TestUI();
window.Show();
// this method call fails
window.Title = _myCom.ComMethod();
window.Closed += ( sender2, e2 )
=> window.Dispatcher.InvokeShutdown();
System.Windows.Threading.Dispatcher.Run();
}
}
class MyCom
{
private dynamic _com;
public MyCom()
{
_com = Activator.CreateInstance(
Type.GetTypeFromProgID( "Excel.Application" ) );
}
public string ComMethod()
{
return (string) _com.Version;
}
}
}
答案 0 :(得分:4)
问题是程序的启动线程。它创建COM对象,启动一个线程,然后退出。作为清除主线程的一部分,.NET调用CoUninitialize(),这是COM对象的结束。获得该错误是预期的结果。
让你的主要启动线程退出是没有意义的。让它现在通过你自己的线程完成工作,问题解决了。
答案 1 :(得分:2)
通常这是因为底层COM对象已从其包装器中释放 - 当您通过Marshal.Release手动释放它或者托管托管包装器时会发生这种情况。在错误的线程上使用它只会导致对COM对象的任何调用实际发生在它创建的线程上 - 我在过去曾被它蜇过,它具有线程亲和力来执行。
您似乎没有处置包装器,但我不确定动态变量的影响是什么。
您是否尝试将线程公寓状态更改为MTA?
答案 2 :(得分:1)
很抱歉也许没有直接回答你的问题。这仅仅是以不同方式处理它的建议。希望它有所帮助。
使用COM与Excel互操作有很多陷阱 - 我认为不是与COM直接相关,而是Excel COM的实现方式。
我在与Excel(以及MsProject)的COM互操作方面苦苦挣扎。对于Excel,唯一的好解决方案是一个专用线程,用于处理从创建到终止的整个Excel通信。 Excel API中存在一些设计缺陷。一些方法调用不是无状态的,这意味着两个线程将很难使这些东西工作。将所有通信委托给一个线程并自己处理与其他线程的通信会更安全。
除此之外,您用于通信的线程也必须具有en / US文化(LCID issues)。这通常会产生另一条消息:
旧格式或无效类型库
但对您来说可能有用。
答案 3 :(得分:1)
尝试为DispatcherObject继承MyCom类。启动其他线程后,执行_myCom.Dispatcher.Run()。当你想与你的COM对象交谈时,只需做一个_myCom.Dispatcher.BeginInvoke / Invoke。