1

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?

1 Answers1

2

DOH! Problem with my code in getFoobar(). Instead of:

IntPtr ptr = handle.AddrOfPinnedObject();

I needded:

IntPtr ptr = GCHandle.ToIntPtr(handle);

This appears to work.