RSCG – NativeObjects

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


Posted

in

,

by

Tags: