using System; using System.Collections.Generic; using System.Linq; using System.Runtime.ConstrainedExecution; using System.Runtime.InteropServices; using System.Text; using System.Threading; namespace CPF.Windows.MicroCom { public unsafe class MicroComProxyBase : CriticalFinalizerObject, IUnknown { private IntPtr _nativePointer; private bool _ownsHandle; private SynchronizationContext _synchronizationContext; public IntPtr NativePointer { get { if (_nativePointer == IntPtr.Zero) throw new ObjectDisposedException(this.GetType().FullName); return _nativePointer; } } public void*** PPV => (void***)NativePointer; public MicroComProxyBase(IntPtr nativePointer, bool ownsHandle) { _nativePointer = nativePointer; _ownsHandle = ownsHandle; _synchronizationContext = SynchronizationContext.Current; if (!_ownsHandle) GC.SuppressFinalize(this); } protected virtual int VTableSize => 3; public void AddRef() { LocalInterop.CalliStdCallvoid(PPV, (*PPV)[1]); } public void Release() { LocalInterop.CalliStdCallvoid(PPV, (*PPV)[2]); } public int QueryInterface(Guid guid, out IntPtr ppv) { IntPtr r = default; var rv = LocalInterop.CalliStdCallint(PPV, &guid, &r, (*PPV)[0]); ppv = r; return rv; } public T QueryInterface() where T : IUnknown { var guid = MicroComRuntime.GetGuidFor(typeof(T)); var rv = QueryInterface(guid, out var ppv); if (rv == 0) return (T)MicroComRuntime.CreateProxyFor(typeof(T), ppv, true); throw new COMException("QueryInterface failed", rv); } public bool IsDisposed => _nativePointer == IntPtr.Zero; protected virtual void Dispose(bool disposing) { if (_nativePointer == null) return; if (_ownsHandle) { Release(); _ownsHandle = false; } _nativePointer = IntPtr.Zero; GC.SuppressFinalize(this); } public void Dispose() => Dispose(true); public bool OwnsHandle => _ownsHandle; public void EnsureOwned() { if (!_ownsHandle) { GC.ReRegisterForFinalize(true); AddRef(); _ownsHandle = true; } } private static readonly SendOrPostCallback _disposeDelegate = DisposeOnContext; private static void DisposeOnContext(object state) { (state as MicroComProxyBase)?.Dispose(false); } ~MicroComProxyBase() { if (!_ownsHandle) return; if (_synchronizationContext == null) Dispose(); else _synchronizationContext.Post(_disposeDelegate, this); } } }