CPF/CPF.Windows/MicroCom/MicroComProxyBase.cs

114 lines
3.1 KiB
C#
Raw Permalink Normal View History

2023-11-21 23:05:03 +08:00
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<T>() 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);
}
}
}