Created
November 27, 2021 16:15
-
-
Save rickbrew/917750a21997447707139d8e31410907 to your computer and use it in GitHub Desktop.
First attempt at direct COM interop in C#
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[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