I am developing a common c# COM component (dll) which can be consumed by any COM client. 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,
For example,
Below are 2 interfaces exposed by my c# dll
interface IMyInInterface
{
void Initialize(ICallback object);
}
interface ICallback
{
void doSomething();
}
my c# dll implements IMyInInterface .
So client application calls Initialize method and passes ICallback pointer to IMyInInterface implementation .
My client is C++ atl dll which has implementation for ICallback.
C++ Call:
....
/* Code to create callbackImpl goes here */
MyImplObj.Initialize(callbackImpl);
My c# dll is wpf dll. So when ICallback object is accessed in UI thread then it can able to call ICallback.Dosomething().It works fine.
C# Implementation:
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.
When the ICallback.Dosomething() is called from background worker as below, I am getting exception as the Interface not found,
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'. 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)).
Stack Trace:
at System.StubHelpers.StubHelpers.GetCOMIPFromRCW(Object objSrc, IntPtr pCPCMD, IntPtr& ppTarget, Boolean& pfNeedsRelease) at MyDLL.ICallback.DoSomething()
How to use com objects from Background worker? Even I tried to do something from Thread with appartment set as STA. the same error occurs.
I found the cause for this issue, The issue is due to BackgroundWorker uses Appartment as MTA. When I tried to get apartment state inside the Worker_DoWork using System.Threading.Thread.CurrentThread.GetApartmentState() method, it returned 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.
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. This ensures sychronizationContext of Task as same as current context.