Skip to content

Instantly share code, notes, and snippets.

@rickbrew
Created November 27, 2021 16:15
Show Gist options
  • Save rickbrew/917750a21997447707139d8e31410907 to your computer and use it in GitHub Desktop.
Save rickbrew/917750a21997447707139d8e31410907 to your computer and use it in GitHub Desktop.
First attempt at direct COM interop in C#
[Guid("8A5E0B48-E799-45d2-B714-3A48F48C2615")]
internal unsafe struct IFxObjectRef
: IFxObjectRef.Interface
{
public interface Interface
: IUnknown.Interface
{
unsafe HRESULT GetObject(GCHandle* gcHandle);
}
public struct Vtbl
{
public delegate* unmanaged<IFxObjectRef*, Guid*, void**, int> QueryInterface;
public delegate* unmanaged<IFxObjectRef*, uint> AddRef;
public delegate* unmanaged<IFxObjectRef*, uint> Release;
public delegate* unmanaged<IFxObjectRef*, GCHandle*, int> GetObject;
}
public unsafe void** lpVtbl;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public HRESULT QueryInterface(Guid* riid, void** ppvObject)
{
return ((delegate* unmanaged<IFxObjectRef*, Guid*, void**, int>)(*this.lpVtbl))((IFxObjectRef*)Unsafe.AsPointer(ref this), riid, ppvObject);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public uint AddRef()
{
return ((delegate* unmanaged<IFxObjectRef*, uint>)this.lpVtbl[1])((IFxObjectRef*)Unsafe.AsPointer(ref this));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public uint Release()
{
return ((delegate* unmanaged<IFxObjectRef*, uint>)this.lpVtbl[2])((IFxObjectRef*)Unsafe.AsPointer(ref this));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public HRESULT GetObject(GCHandle* gcHandle)
{
return ((delegate* unmanaged<IFxObjectRef*, GCHandle*, int>)this.lpVtbl[3])((IFxObjectRef*)Unsafe.AsPointer(ref this), gcHandle);
}
}
internal static unsafe class FxObjectRefExtensions
{
public static object? GetObject<TIFxObjectRef>(this ref TIFxObjectRef fxObjectRef)
where TIFxObjectRef : unmanaged, IFxObjectRef.Interface
{
GCHandle gcHandle = default;
HRESULT hr = fxObjectRef.GetObject(&gcHandle);
hr.ThrowOnError();
return gcHandle.Target;
}
}
internal unsafe struct CFxObjectRef
: IFxObjectRef.Interface
{
private static readonly Vtbl* vtblStatic = CreateVtbl();
private static Vtbl* CreateVtbl()
{
Vtbl* vtbl = (Vtbl*)RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(CFxObjectRef), sizeof(CFxObjectRef.Vtbl));
vtbl->QueryInterface = &QueryInterface;
vtbl->AddRef = &AddRef;
vtbl->Release = &Release;
vtbl->GetObject = &GetObject;
return vtbl;
}
public struct Vtbl
{
public delegate* unmanaged<CFxObjectRef*, Guid*, void**, int> QueryInterface;
public delegate* unmanaged<CFxObjectRef*, uint> AddRef;
public delegate* unmanaged<CFxObjectRef*, uint> Release;
public delegate* unmanaged<CFxObjectRef*, GCHandle*, int> GetObject;
}
public static HRESULT Create(object @object, bool disposeOnRelease, Guid* riid, IFxObjectRef** ppFxObjectRef)
{
if (riid == null || ppFxObjectRef == null)
{
return E_POINTER;
}
CFxObjectRef* pInstance = (CFxObjectRef*)NativeMemory.AllocZeroed((nuint)sizeof(CFxObjectRef));
*pInstance = new CFxObjectRef(@object, disposeOnRelease);
HRESULT hr = pInstance->QueryInterface(riid, (void**)ppFxObjectRef);
pInstance->Release();
return hr;
}
private readonly Vtbl* lpVtbl;
private int refCount;
private GCHandle gcHandle;
private bool disposeOnRelease;
private CFxObjectRef(object @object, bool disposeOnRelease)
{
this.lpVtbl = vtblStatic;
this.refCount = 1;
this.gcHandle = GCHandle.Alloc(@object);
this.disposeOnRelease = disposeOnRelease;
}
public HRESULT QueryInterface(Guid* riid, void** ppvObject)
{
if (riid == null || ppvObject == null)
{
return E_POINTER;
}
*ppvObject = null;
Guid iid = *riid;
if (iid == __uuidof<IFxObjectRef>() || iid == __uuidof<IUnknown>())
{
Interlocked.Increment(ref this.refCount);
*ppvObject = Unsafe.AsPointer(ref this);
return S_OK;
}
return E_NOINTERFACE;
}
[UnmanagedCallersOnly]
private static int QueryInterface(CFxObjectRef* self, Guid* riid, void** ppvObject)
{
return self->QueryInterface(riid, ppvObject);
}
public uint AddRef()
{
return (uint)Interlocked.Increment(ref this.refCount);
}
[UnmanagedCallersOnly]
public static uint AddRef(CFxObjectRef* self)
{
return self->AddRef();
}
public uint Release()
{
int newRefCount = Interlocked.Decrement(ref this.refCount);
if (newRefCount == 0)
{
if (this.disposeOnRelease)
{
(this.GetObject() as IDisposable)?.Dispose();
}
this.gcHandle.Free();
NativeMemory.Free(Unsafe.AsPointer(ref this));
}
return (uint)newRefCount;
}
[UnmanagedCallersOnly]
private static uint Release(CFxObjectRef* self)
{
return self->Release();
}
public HRESULT GetObject(GCHandle* pGCHandle)
{
if (pGCHandle == null)
{
return E_POINTER;
}
*pGCHandle = this.gcHandle;
return S_OK;
}
[UnmanagedCallersOnly]
private static unsafe int GetObject(CFxObjectRef* self, GCHandle* pGCHandle)
{
return self->GetObject(pGCHandle);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment