I am getting following error:
"COM object that has been separated from its underlying RCW cannot be used."
I am sure the problem is because COM object is being called not on the thread it has been created - STA. I tried to implement IDisposable but it has not worked for me.
There is a couple of posts dealing with similar problem but which still do not solve my issue:
Is it safe to call an RCW from a finalizer? Release Excel Object In My Destructor
Could anyone post an example/explain how COM object should be correctly accessed from another thread?
Here is minimal code which shows the problem:
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;
}
}
}
The problem is your program's startup thread. It creates the COM object, starts a thread, then exits. As part of the cleanup of that main thread, .NET calls CoUninitialize() and that's the end of the COM object. Getting that error is the expected result.
There's just no point in letting your main startup thread exit like that. Let it do the work now done by your own thread, problem solved.
Usually this is because the underlying COM object has been released from its wrapper - this happens when you manually release it via Marshal.Release or the managed wrapper is disposed. Using it on the wrong thread will simply cause any calls to the COM object to actually occur on the thread it was created on - I was stung by this in the past, it has thread affinity for execution.
You don't appear to be disposing of the wrapper, but I'm not sure what the affect of the dynamic variable will be.
Have you tried changing your thread apartment state to MTA?
Try making your MyCom class inherit for DispatcherObject. After you start up your other thread, do a _myCom.Dispatcher.Run(). When you want to talk to your COM object, just do a _myCom.Dispatcher.BeginInvoke/Invoke.
Sorry for maybe not answering directly your question. This is merely an advice for handling it in a different way. Hope it helps.
With COM interop with Excel there are quite a few pitfalls - not directly related to COM I think, but to how Excel COM is implemented.
I struggled a lot with COM interop with Excel (and also MsProject). For Excel the only good solution was a dedicated thread for handling the whole Excel communication from creation until termination. There are a few design flaws in the Excel API. Some method calls are not stateless, meaning two threads will have a hard time to make the stuff work. It would be safer to delegate all the communication to one thread and handle the communication with other threads yourself.
Beside this, the thread you are using for communication MUST also have the en/US culture (LCID issues). This usually results in an other message:
but might be useful to you to know.