如何在 C# Background worker 中访问 C++ COM 对象?

How to access C++ COM Object in C# Background worker?

我正在开发一个通用的 c# COM 组件 (dll),它可以被任何 COM 客户端使用。作为其中的一部分,我公开了一个名为 ICallback.The 的传出接口,客户端使用我的 dll 将实现这些方法并通过传入接口方法

提供 com 对象

例如,

下面是我的 c# dll 公开的 2 个接口

interface IMyInInterface 
 {
      void Initialize(ICallback object);
 }

 interface ICallback
{
      void doSomething();
}

我的 c# dll 实现了 IMyInInterface 。

因此客户端应用程序调用 Initialize 方法并将 ICallback 指针传递给 IMyInInterface 实现。

我的客户端是 C++ atl dll,它具有 ICallback 的实现。

C++调用:

         .... 
         /* Code to create callbackImpl goes here */

         MyImplObj.Initialize(callbackImpl);

我的 c# dll 是 wpf dll。因此,当在 UI 线程中访问 ICallback 对象时,它可以调用 ICallback.Dosomething()。它工作正常。

C# 实现:

class MyImpl : IMyInterface
{

...
            ICallback _Mycallback = null;

            void Initialize(ICallback callback)
             {
                 _MyCallback = callback         
             }

              Button_Click()
              { 
                 _Mycallback.DoSomething();       **// This works fine**
              } 
}

但是由于 Dosomething() 是一个很长的 运行 任务,我想并行完成这项工作,所以我为此使用了 BackgroundWorker。

当如下从后台工作者调用 ICallback.Dosomething() 时,我收到异常,因为找不到接口,

class MyImpl : IMyInterface
    {

    ...
                ICallback _Mycallback = null;

                void Initialize(ICallback callback)
                 {
                     _MyCallback = callback         
                 }

                  Button_Click()
                  { 
                     //Code to create Background worker //

                    BackgroundWorker bkgrwkr = new BackgroundWorker();
                    bkgrwkr.WorkerReportsProgress = true;
                    bkgrwkr.WorkerSupportsCancellation = true;            
                    bkgrwkr.DoWork += new DoWorkEventHandler(Worker_DoWork);

                    .....

                  } 

                  void Worker_DoWork()
                  {
                        _Mycallback.DoSomething();       **// This Fails**
                  }
    }    

失败并出现未找到接口异常。

Unable to cast COM object of type 'System.__ComObject' to interface type 'ICallback'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{3B7C00E3-C145-4195-B9B4-984EAAC8954D}' failed due to the following error: No such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE)).

堆栈跟踪:

at System.StubHelpers.StubHelpers.GetCOMIPFromRCW(Object objSrc, IntPtr pCPCMD, IntPtr& ppTarget, Boolean& pfNeedsRelease) at MyDLL.ICallback.DoSomething()

如何使用后台工作者的com对象?甚至我也尝试从线程中将公寓设置为 STA 来做一些事情。发生同样的错误。

问题是代码 运行 在 BackgroundWorker 中,运行 位于线程池中的线程上,其中未设置 COM 单元模型。

我建议您从专用线程调用您的界面。在调用 Thread.Start().

之前使用 Thread.SetApartmentState(ApartmentState.STA);

此外,您传递给 Initialize 的对象必须在 相同的 线程中创建。如果不是,则此类对象需要在特殊的 was 中编组。查找更多详细信息 here

我找到了这个问题的原因,这个问题是由于 BackgroundWorker 使用 Appartment 作为 MTA。当我尝试使用 System.Threading.Thread.CurrentThread.GetApartmentState() 方法获取 Worker_DoWork 内的公寓状态时,它返回了 MTA。这是问题的根本原因,

现在我已经使用 Task 解决了问题,如下所示,使用 task 也实现了并行执行。

       Button_Click()
       {  
           Task workerTask = Task.Factory.StartNew(
           () =>
           {
               DoWorkDelegate();
           }
           , CancellationToken.None
           , TaskCreationOptions.None
           , TaskScheduler.FromCurrentSynchronizationContext()
           ); 

           workerTask.wait();
       }

       void DoWorkDelegate()
       {
        _Mycallback.DoSomething();     // This works 
       }

传递 TaskScheduler.FromCurrentSynchronizationContext() 可解决问题。这确保 Task 的 sychronizationContext 与当前上下文相同。