我正在开发一个通用的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实现。
我的客户端是具有ICallback实现的C ++ atl dll。
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”。此操作失败,因为QueryInterface 调用带有IID的接口的COM组件 “ {3B7C00E3-C145-4195-B9B4-984EAAC8954D}”由于以下原因而失败 错误:不支持此类接口(HRESULT的异常:0x80004002 (E_NOINTERFACE)。
堆栈跟踪:
在System.StubHelpers.StubHelpers.GetCOMIPFromRCW(Object objSrc, IntPtr pCPCMD,IntPtr&ppTarget,Boolean&pfNeedsRelease) MyDLL.ICallback.DoSomething()
如何使用Background Worker中的com对象?甚至我也尝试通过Thread将单元设置为STA来做某事。发生相同的错误。
答案 0 :(得分:0)
我找到了此问题的原因,该问题是由于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与当前上下文相同。
答案 1 :(得分:-1)
问题在于,在filter(val => {
const equal = isEqual(cachedValue, val);
cachedValue = val;
return !equal;
})
中运行的代码在未设置COM公寓模型的线程池中的线程上运行。
我建议您从专用线程调用接口。调用BackgroundWorker
之前,先使用Thread.SetApartmentState(ApartmentState.STA);
。
此外,必须在相同线程中创建传递给Thread.Start()
的对象。如果没有,则需要将这些对象编组为特殊的对象。查找更多详细信息here。