简体   繁体   English

如何在C#后台工作程序中访问C ++ COM对象?

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

I am developing a common c# COM component (dll) which can be consumed by any COM client. 我正在开发一个通用的c#COM组件(dll),任何COM客户端都可以使用它。 As part of this I m exposing a outgoing interface called ICallback.The client which is using my dll will implement the methods and provide the com object via incoming interface method, 在此过程中,我公开了一个名为ICallback的传出接口。使用我的dll的客户端将实现这些方法,并通过传入接口方法提供com对象,

For example, 例如,

Below are 2 interfaces exposed by my c# dll 以下是我的C#dll公开的2个接口

interface IMyInInterface 
 {
      void Initialize(ICallback object);
 }

 interface ICallback
{
      void doSomething();
}

my c# dll implements IMyInInterface . 我的C#dll实现IMyInInterface。

So client application calls Initialize method and passes ICallback pointer to IMyInInterface implementation . 因此,客户端应用程序调用Initialize方法,并将ICallback指针传递给IMyInInterface实现。

My client is C++ atl dll which has implementation for ICallback. 我的客户端是具有ICallback实现的C ++ atl dll。

C++ Call: C ++调用:

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

         MyImplObj.Initialize(callbackImpl);

My c# dll is wpf dll. 我的C#dll是wpf dll。 So when ICallback object is accessed in UI thread then it can able to call ICallback.Dosomething().It works fine. 因此,当在UI线程中访问ICallback对象时,它可以调用ICallback.Dosomething()。

C# Implementation: C#实现:

class MyImpl : IMyInterface
{

...
            ICallback _Mycallback = null;

            void Initialize(ICallback callback)
             {
                 _MyCallback = callback         
             }

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

But Since Dosomething() is a long running task, I wanted to do this work in parllel, so I am using BackgroundWorker for this. 但是由于Dosomething()是一项长期运行的任务,因此我想并行执行此工作,因此我为此使用了BackgroundWorker。

When the ICallback.Dosomething() is called from background worker as below, I am getting exception as the Interface not found, 当从后台工作程序调用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**
                  }
    }    

it fails with interface not found exception. 它失败,接口未找到异常。

Unable to cast COM object of type 'System.__ComObject' to interface type 'ICallback'. 无法将类型为“ System .__ ComObject”的COM对象转换为接口类型为“ 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)). 此操作失败,因为具有以下错误的IID为'{3B7C00E3-C145-4195-B9B4-984EAAC8954D}'的接口的COM组件上的QueryInterface调用由于以下错误而失败:不支持此类接口(HRESULT的异常:0x80004002(E_NOINTERFACE)) 。

Stack Trace: 堆栈跟踪:

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

How to use com objects from Background worker? 如何使用Background Worker中的com对象? Even I tried to do something from Thread with appartment set as STA. 甚至我也尝试通过Thread将单元设置为STA来做某事。 the same error occurs. 发生相同的错误。

I found the cause for this issue, The issue is due to BackgroundWorker uses Appartment as MTA. 我发现了此问题的原因,该问题是由于BackgroundWorker使用Appartment作为MTA引起的。 When I tried to get apartment state inside the Worker_DoWork using System.Threading.Thread.CurrentThread.GetApartmentState() method, it returned MTA. 当我尝试使用System.Threading.Thread.CurrentThread.GetApartmentState()方法获取Worker_DoWork内的公寓状态时,它返回MTA。 This is root cause for the issue, 这是问题的根本原因,

Now I have resolved the issue using Task as below, Using task the parallel execution also achieved. 现在,我已经按照以下方法使用Task解决了这个问题,使用Task还可以实现并行执行。

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

           workerTask.wait();
       }

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

Passing TaskScheduler.FromCurrentSynchronizationContext() resolves the issue. 传递TaskScheduler.FromCurrentSynchronizationContext()可解决此问题。 This ensures sychronizationContext of Task as same as current context. 这样可以确保Task的sychronizationContext与当前上下文相同。

The problem is that code, run in a BackgroundWorker , runs on a thread in the thread pool, where the COM apartment model is not set. 问题在于,在BackgroundWorker中运行的代码在未设置COM公寓模型的线程池中的线程上运行。

I recommend you call your interface from a dedicated thread. 我建议您从专用线程调用接口。 Use Thread.SetApartmentState(ApartmentState.STA); 使用Thread.SetApartmentState(ApartmentState.STA); before calling Thread.Start() . 在调用Thread.Start()之前。

Also, the object that you pass to Initialize must be created in the same thread. 此外,必须在同一线程中创建传递给Initialize的对象。 If not, such objects need to be marshaled in a special was. 如果没有,则需要将这些对象编组为特殊的对象。 Find more details here . 在此处查找更多详细信息。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM