I've created a simple c# COM component that is meant to be called by a C++ MFC application. It works but has an undesired side-effect.
The C++ client calls the c# component using it's main GUI thread, so I can't block it because the C# code is making Database operations that may take longer. That's why i need async or a thread for this...
This is the C# code, simplified:
public async void ShowOverviewDialogAsync()
{
var w = new Window();
var dbOperationOk = await LongDbOperaionAsync();
w.ShowDialog();
}
Well, the C++ code goes in, and returns after the call to async which is expected. Here is the C++ calling code:
HRESULT hr = cSharpCom.CoCreateInstance(__uuidof(CSharpCom));
if (SUCCEEDED(hr))
{
cSharpCom->ShowOverviewDialogAsync();
}
// continues without waiting for the dialog close...
The thing is, after this call, the C++ code continues to do its thing and the cSharpCom object goes out of scope. So the only way to call a Release is to make the c# object a member and on destructing or creating a new one, do a release call:
if (_cSharpCom != NULL) _cSharpCom->Release();
HRESULT hr = _cSharpCom.CoCreateInstance(__uuidof(CSharpCom));
if (SUCCEEDED(hr))
{
_cSharpCom->ShowOverviewDialogAsync();
}
This is the first Drawback. The second drawback is that the modal C# window is not a real Modal window as the message pooling of the MFC(C++) continues as normal, which is actually undesired (Right?)
These are the current ideas:
- return a HWND to the calling code and somehow make the modal call using the C++ Cwnd:RunModalLoop.
- return some kind of event to the calling code, that can be waited upon without blocking the GUI thread.
- set a callback that will make sure the COM object will be released.
- override the Task class so that it returns a COM aware Interface that allows the C++ code to wait on something (see 2. and 3.)
Number 4 is what I will probably try out. The COM signature would look like:
HRESULT _stdcall ShowOverviewDialogAsync([out, retval] IUnknown** pRetVal);
Just for the lack of completeness, if the method has no async, the MFC C++ GUI hread works as expected and shows the Window in a modal way:
public void ShowOverviewDialogAsync()
{
var w = new Window();
var dbOperationOk = LongDbOperaion();
w.ShowDialog(); // C++ will wait here - non-blocking
}
If you are asking why my async method is returning void. Well, it's defined in an interface and I can't use Task as a return value, unless I go for the solution 4 above.
[ComVisible(true)]
[Guid("XXXXXX-xxxx-xxx-xxx-XXXXX")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface ICSharpCom
{
/// <summary>
/// Displays the dialog in async mode
/// </summary>
[DispId(0)]
void ShowOverviewDialogAsync();
}
Besides, COM interop does not accept generics like Task.
How to test it. Create a MFC application with one button that calls the c# com as defined.
Any ideas appreciated.
cheers, Marco