0

我正在开发一个通用的 c# COM 组件 (dll),任何 COM 客户端都可以使用它。作为其中的一部分,我公开了一个名为 ICallback 的传出接口。使用我的 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**
                  }
    }    

它因找不到接口异常而失败。

无法将“System.__ComObject”类型的 COM 对象转换为接口类型“ICallback”。此操作失败,因为 IID 为“{3B7C00E3-C145-4195-B9B4-984EAAC8954D}”的接口的 COM 组件上的 QueryInterface 调用因以下错误而失败:不支持此类接口(来自 HRESULT 的异常:0x80004002 (E_NOINTERFACE)) .

堆栈跟踪:

在 System.StubHelpers.StubHelpers.GetCOMIPFromRCW(对象 objSrc,IntPtr pCPCMD,IntPtr& ppTarget,Boolean& pfNeedsRelease)在 MyDLL.ICallback.DoSomething()

如何使用来自后台工作人员的 com 对象?即使我尝试从 Thread 做一些事情,并将公寓设置为 STA。发生同样的错误。

4

2 回答 2

0

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

现在我已经使用以下任务解决了这个问题,使用任务也实现了并行执行。

       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 与当前上下文相同。

于 2019-03-03T04:21:35.530 回答
-1

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

我建议您从专用线程调用您的接口。Thread.SetApartmentState(ApartmentState.STA);调用前使用Thread.Start()

此外,您传递给的对象Initialize必须在同一个线程中创建。如果没有,则需要将此类对象编组在一个特殊的位置。在此处查找更多详细信息。

于 2019-02-24T21:18:01.937 回答