Skip to content

Instantly share code, notes, and snippets.

@2bam
Last active March 31, 2022 01:25
Show Gist options
  • Save 2bam/b8ad3853c2cee42d07ec6dd074deb82a to your computer and use it in GitHub Desktop.
Save 2bam/b8ad3853c2cee42d07ec6dd074deb82a to your computer and use it in GitHub Desktop.
Acceptable way to pin/fix structs for C#-C interop including swap chain arrays by using-Dispose pattern without GetPinnableReference because that's garbage.
internal class CInteropProxy {
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
unsafe delegate int C_stepDelegate(World.Pinned* world);
C_stepDelegate? c_step; // Set with kernel32.dll GetProcAddress()
public void Step(in World world)
{
unsafe
{
using (var pinWorld = world.Pin()) // using() instead of fixed()
{
c_step?.Invoke(pinWorld);
}
}
}
}
public unsafe class Pin<T> : IDisposable where T : unmanaged
{
public T value;
GCHandle[] _pinHandles;
int _pins;
IntPtr _structPtr;
public Pin()
{
_structPtr = Marshal.AllocHGlobal(sizeof(T));
_pinHandles = new GCHandle[32]; // TODO: static pooled
_pins = 0;
value = default(T);
}
public unsafe T* AlsoPin<T>(ref T[] managedReference) where T : unmanaged
{
GCHandle handle;
_pinHandles[_pins++] = handle = GCHandle.Alloc(managedReference, GCHandleType.Pinned);
return (T*)handle.AddrOfPinnedObject();
}
public static implicit operator T*(Pin<T> pin) => pin.Ptr();
T* Ptr()
{
Marshal.StructureToPtr(value, _structPtr, false);
return (T*)_structPtr;
}
// This will dispose pinned/unamanged memory
public void Dispose()
{
while (_pins != 0)
{
_pinHandles[--_pins].Free();
}
Marshal.FreeHGlobal(_structPtr);
}
}
public struct World
{
public Int32 width;
public Int32 height;
public Cell[] before;
public Cell[] after;
[StructLayout(LayoutKind.Sequential)]
public unsafe struct Pinned
{
public Int32 width;
public Int32 height;
public Cell* before;
public Cell* after;
}
public unsafe Pin<Pinned> Pin()
{
var pin = new Pin<Pinned>();
pin.value = new Pinned
{
width = width,
height = height,
before = pin.AlsoPin(ref before),
after = pin.AlsoPin(ref after)
};
return pin;
}
}
@2bam
Copy link
Author

2bam commented Mar 30, 2022

Note: width and height are read-only on the C side because they don't get copied back (the same way the actual references to the arrays and not the contents can't change neither).
I'm still thinking of an elegant solution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment