[英]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.