I am prototyping using the HostWitHostFxr sample which allows a native process to host .NET Core using the nethost and hostfxr libraries. https://github.com/dotnet/samples/tree/master/core/hosting/HostWithHostFxr
I am trying to return an instance of a managed object from C# to C++. C++ then calls another C# method to do something with the managed object and release it.
Right now I am failing in the 2nd step with: Unhandled exception. System.NullReferenceException: Object reference not set to an instance of an object.
From the research I had done my understanding is that to return a managed C# object reference to C++ I would need to use GCHandle.Alloc() with GCHandleType.Pinned to pin the object.
I modified the C# sample Lib.cs to add a new Foobar class:
// prototype
[StructLayout(LayoutKind.Sequential)]
public class Foobar
{
public Foobar()
{
Console.WriteLine("Foobar constr");
}
~Foobar()
{
Console.WriteLine("Foobar destr");
}
public bool do_something()
{
Console.WriteLine("Foobar.do_something()");
return true;
}
}
I added a method to return an instance of Foobar:
// prototype: from static method return a Foobar instance
public delegate IntPtr getFoobarDelegate();
public static IntPtr getFoobar() {
Console.WriteLine("getFoobar()");
Foobar obj = new Foobar();
GCHandle handle = GCHandle.Alloc(obj, GCHandleType.Pinned);
IntPtr ptr = handle.AddrOfPinnedObject();
Console.WriteLine($"getFoobar() return {ptr}");
return ptr;
}
I added a method to do something with the instance of Foobar and free it:
// prototype: from static method use and destroy a Foobar instance
public delegate bool freeFoobarDelegate(IntPtr foo);
public static bool freeFoobar(IntPtr foo) {
Console.WriteLine($"freeFoobar() foo {foo}");
if (foo == IntPtr.Zero) return false;
GCHandle gch = GCHandle.FromIntPtr(foo);
Foobar a = (gch.Target as Foobar);
a.do_something();
gch.Free();
foo = IntPtr.Zero;
return true;
}
On the C++ side I modified nativehost.cpp to add this code to get the Foobar instance:
// prototype: call C# getFoobar()
typedef intptr_t (CORECLR_DELEGATE_CALLTYPE *getFoobar_fn)();
getFoobar_fn getFoobar = nullptr;
rc = load_assembly_and_get_function_pointer(
dotnetlib_path.c_str(),
dotnet_type,
STR("getFoobar") /*method_name*/,
STR("DotNetLib.Lib+getFoobarDelegate, DotNetLib") /*delegate_type_name*/,
nullptr,
(void**)&getFoobar);
assert(rc == 0 && getFoobar != nullptr && "Failure: load_assembly_and_get_function_pointer()");
intptr_t scorer = getFoobar();
assert(scorer != 0 && "Failure: getFoobar()");
And this to do something with the Foobar instance:
// prototype: call C# freeFoobar()
typedef bool (CORECLR_DELEGATE_CALLTYPE *freeFoobar_fn)(intptr_t);
freeFoobar_fn freeFoobar = nullptr;
rc = load_assembly_and_get_function_pointer(
dotnetlib_path.c_str(),
dotnet_type,
STR("freeFoobar") /*method_name*/,
STR("DotNetLib.Lib+freeFoobarDelegate, DotNetLib") /*delegate_type_name*/,
nullptr,
(void**)&freeFoobar);
assert(rc == 0 && freeFoobar != nullptr && "Failure: load_assembly_and_get_function_pointer()");
bool ret = freeFoobar(scorer);
assert(ret != 0 && "Failure: freeFoobar()");
What am I missing to get this working?