WPF Thread: “COM object that has been separated fr

2019-08-25 06:03发布

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;
        }
    }    
}

4条回答
smile是对你的礼貌
2楼-- · 2019-08-25 06:16

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.

查看更多
兄弟一词,经得起流年.
3楼-- · 2019-08-25 06:33

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?

查看更多
干净又极端
4楼-- · 2019-08-25 06:35

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.

查看更多
老娘就宠你
5楼-- · 2019-08-25 06:39

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:

Old format or invalid type library

but might be useful to you to know.

查看更多
登录 后发表回答