Closed RickStrahl closed 4 years ago
cc @AaronRobinsonMSFT
@RickStrahl Lots of good questions here. An alternative approach to activating a class is to use COM, which works in .NET Core 3.0 so you can consider that as well. A sample with various options can be found here.
As far as what is going wrong in this example returning a struct
from the managed callback and converting it to an IUnknown
based interface isn't going to work for a myriad of reasons. Instead I would suggest changing the managed callback to something similar to the following:
public static int Activate([MarshalAs(UnmanagedType.IDispatch)] out object t)
{
t = null;
try
{
t = new object();
}
catch (Exception e)
{
return e.HResult;
}
return 0;
}
The native side for consumption would be similar to the following:
HRESULT (__stdcall *activate)(IDispatch **) = nullptr;
// Get handle to managed delegate
...
CComPtr<IDispatch> obj;
HRESULT hr = activate(&obj);
if (FAILED(hr))
throw hr;
@AaronRobinsonMSFT - thank you. So close, yet oh so far :-). I had tried several of the Unmanaged types but totally missed the IUnknown
setting (I was looking for IDispatch
I guess). IAC it works with this mapping!
Just for reference here's what worked:
public static int CreatewwDotnetBridgeByRef([MarshalAs(UnmanagedType.IUnknown)] ref object instance)
{
try
{
instance = new wwDotNetBridge();
}
catch (Exception ex)
{
instance = null;
return ex.HResult;
}
return 0;
}
Notice I'm using IUnknown
as you orginally had it - in .NET Core 3.0 Preview 6 I don't see an UnmanagedType.IDispatch
.
Here's the native code (with the rest of the code from the CoreClrHostExample):
typedef HRESULT(__stdcall* createWwDotnetBridgeHandler)(CComPtr<IUnknown> * ptr);
createWwDotnetBridgeHandler createWwDotnetBridge;
// The assembly name passed in the third parameter is a managed assembly name
// as described at https://docs.microsoft.com/dotnet/framework/app-domains/assembly-names
hr = createManagedDelegate(
hostHandle,
domainId,
"wwDotNetBridge, Version=7.6.0.0, Culture=neutral, PublicKeyToken=null",
"Westwind.WebConnection.wwDotnetBridgeFactory",
"CreatewwDotnetBridgeByRef",
(VOID **)& createWwDotnetBridge);
// </Snippet5>
if (hr >= 0)
{
printf("Managed delegate created\n");
}
else
{
printf("coreclr_create_delegate failed - status: 0x%08x\n", hr);
return -1;
}
CComPtr<IUnknown> obj;
DWORD result = createWwDotnetBridge((CComPtr<IUnknown> *) &obj);
CComPtr<IDispatch> disp;
hr = obj->QueryInterface(&disp);
if (FAILED(hr))
throw hr;
// this is what I'm after
IDispatch* p = disp;
// Verifying COM instance: Call Com instance
CComVariant result2;
CComVariant parm1;
parm1.bstrVal = CComBSTR(L"Test");
hr = disp.Invoke1(L"Ping",&parm1,&result2);
if (FAILED(hr))
throw hr;
I'm working on Core CLR Host integration code, where I create a runtime host and then pass back a .NET object to a COM client. However, I can't seem to figure out how to capture the returned object reference effectively and pass back an IDispatch pointer to the host application.
I'm probably not casting/marshalling correctly, but after trying a number of different combinations I can't seem to find the right incantation to make this work.
I started with the CoreClr Hosting Sample and then essentially changed out the delegate to call my own .NET assembly that acts as a factory loader. I'm using
initializeCoreClr
and then a custom Managed Delegate to retrieve the .NET object. The call works - no errors and I can verify that method is called and returning the proper simple value. But I can't seem to pick up the resulting .NET Object in a usable way.The .NET method I'm calling is very simple and just a factory for a specific class:
The relevant C++ code using CoreClrLoader is:
This code fails with an access exception trying to unwrap the value:
I can verify that the
managedDelegate()
call happens as I get the1
result value and I see the logged entry on disk. I assume the object reference is set as well as I do get a value back in the reference. But the value is always0x09
which seems like a base interface (IUnknown?). I've also returned the raw pointer(void **)
which gives the same result.So my question is this:
How should I cast the .NET object reference so I can capture it in the C++ code and... for bonus points how can I unwrap it properly into an IDispatch pointer value (an DWORD) I can pass back to my host application?
Any help would be appreciated.