RSCG – NativeObjects
 
 
| name | NativeObjects | 
| nuget | https://www.nuget.org/packages/NativeObjects/ | 
| link | https://github.com/kevingosse/NativeObjects | 
| author | Kevin Gosse | 
Object to IntPtr and back
This is how you can use NativeObjects .
The code that you start with is
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net8.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> <AllowUnsafeBlocks>true</AllowUnsafeBlocks> </PropertyGroup> <ItemGroup> <PackageReference Include="NativeObjects" Version="1.3.0" OutputItemType="Analyzer" ReferenceOutputAssembly="false" /> </ItemGroup> <PropertyGroup> <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles> <CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath> </PropertyGroup> </Project>
The code that you will use is
using NativeObjectsDemo;
Person p = new Person();
p.DateOfBirth= new DateTime(1970,4,16);
using (var nativ = NativeObjects.IPerson.Wrap(p))
{
    SomeNativeCode((IntPtr)nativ.Object);
}
static void SomeNativeCode(IntPtr nativePerson)
{
    var p = NativeObjects.IPerson.Wrap(nativePerson);
    Console.WriteLine($"Age: {p.CalculateAge()}");
}
namespace NativeObjectsDemo;
[NativeObject]
public interface IPerson
{
    public int CalculateAge();
}
class Person : IPerson
{
    public DateTime DateOfBirth { get; set; }
    public int CalculateAge()
    {
        return (int)DateTime.Now.Subtract(DateOfBirth).TotalDays / 365;
    }
}
The code that is generated is
using System;
[AttributeUsage(AttributeTargets.Interface, Inherited = false, AllowMultiple = false)]
internal class NativeObjectAttribute : Attribute { }
[AttributeUsage(AttributeTargets.Assembly, Inherited = false, AllowMultiple = false)]
internal class NativeObjectsNamespaceAttribute : Attribute
{
    public NativeObjectsNamespaceAttribute(string name) { }
}
namespace NativeObjects
{
    
    using System;
    using System.Runtime.CompilerServices;
    using System.Runtime.InteropServices;
    public unsafe class IPerson : IDisposable
    {
        private IPerson(NativeObjectsDemo.IPerson implementation)
        {
            const int delegateCount = 1;
            var vtable = (IntPtr*)NativeMemory.Alloc((nuint)delegateCount, (nuint)IntPtr.Size);
            *(vtable + 0) = (IntPtr)(delegate* unmanaged<IntPtr*, int>)&Exports.CalculateAge;
            var obj = (IntPtr*)NativeMemory.Alloc((nuint)2, (nuint)IntPtr.Size);
            *obj = (IntPtr)vtable;
            var handle = GCHandle.Alloc(implementation);
            *(obj + 1) = GCHandle.ToIntPtr(handle);
            Object = (IntPtr)obj;
        }
        public IntPtr Object { get; private set; }
        public static IPerson Wrap(NativeObjectsDemo.IPerson implementation) => new(implementation);
        public static IPersonInvoker Wrap(IntPtr ptr) => new(ptr);
        public static implicit operator IntPtr(IPerson stub) => stub.Object;
        public void Dispose()
        {
            if (Object != IntPtr.Zero)
            {
                var target = (void**)Object;
                NativeMemory.Free(*target);
                NativeMemory.Free(target);
                Object = IntPtr.Zero;
            }
        }
        private static class Exports
        {
            [UnmanagedCallersOnly]
            public static int CalculateAge(IntPtr* self)
            {
                var handle = GCHandle.FromIntPtr(*(self + 1));
                var obj = (NativeObjectsDemo.IPerson)handle.Target;
                var result = obj.CalculateAge();
                return result;
            }
        }
    }
    public unsafe struct IPersonInvoker
    {
        private readonly IntPtr _implementation;
        public IPersonInvoker(IntPtr implementation)
        {
            _implementation = implementation;
        }
        public static implicit operator IntPtr(IPersonInvoker invoker) => invoker._implementation;
        private nint* VTable => (nint*)*(nint*)_implementation;
        public  int CalculateAge()
        {
            var __func__ = (delegate* unmanaged[Stdcall]<IntPtr,  int>)*(VTable + 0);
            return  __func__(_implementation);
        }
 
    }
}
Code and pdf at
https://ignatandrei.github.io/RSCG_Examples/v2/docs/NativeObjects