MicroComProxyBase.cs 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. using System;
  2. using System.Runtime.ConstrainedExecution;
  3. using System.Runtime.InteropServices;
  4. using System.Threading;
  5. namespace Avalonia.MicroCom
  6. {
  7. public unsafe class MicroComProxyBase : CriticalFinalizerObject, IUnknown
  8. {
  9. private IntPtr _nativePointer;
  10. private bool _ownsHandle;
  11. private SynchronizationContext _synchronizationContext;
  12. public IntPtr NativePointer
  13. {
  14. get
  15. {
  16. if (_nativePointer == IntPtr.Zero)
  17. throw new ObjectDisposedException(this.GetType().FullName);
  18. return _nativePointer;
  19. }
  20. }
  21. public void*** PPV => (void***)NativePointer;
  22. public MicroComProxyBase(IntPtr nativePointer, bool ownsHandle)
  23. {
  24. _nativePointer = nativePointer;
  25. _ownsHandle = ownsHandle;
  26. _synchronizationContext = SynchronizationContext.Current;
  27. if(!_ownsHandle)
  28. GC.SuppressFinalize(this);
  29. }
  30. protected virtual int VTableSize => 3;
  31. public void AddRef()
  32. {
  33. LocalInterop.CalliStdCallvoid(PPV, (*PPV)[1]);
  34. }
  35. public void Release()
  36. {
  37. LocalInterop.CalliStdCallvoid(PPV, (*PPV)[2]);
  38. }
  39. public int QueryInterface(Guid guid, out IntPtr ppv)
  40. {
  41. IntPtr r = default;
  42. var rv = LocalInterop.CalliStdCallint(PPV, &guid, &r, (*PPV)[0]);
  43. ppv = r;
  44. return rv;
  45. }
  46. public T QueryInterface<T>() where T : IUnknown
  47. {
  48. var guid = MicroComRuntime.GetGuidFor(typeof(T));
  49. var rv = QueryInterface(guid, out var ppv);
  50. if (rv == 0)
  51. return (T)MicroComRuntime.CreateProxyFor(typeof(T), ppv, true);
  52. throw new COMException("QueryInterface failed", rv);
  53. }
  54. public bool IsDisposed => _nativePointer == IntPtr.Zero;
  55. protected virtual void Dispose(bool disposing)
  56. {
  57. if(_nativePointer == null)
  58. return;
  59. if (_ownsHandle)
  60. {
  61. Release();
  62. _ownsHandle = false;
  63. }
  64. _nativePointer = IntPtr.Zero;
  65. GC.SuppressFinalize(this);
  66. }
  67. public void Dispose() => Dispose(true);
  68. public bool OwnsHandle => _ownsHandle;
  69. public void EnsureOwned()
  70. {
  71. if (!_ownsHandle)
  72. {
  73. GC.ReRegisterForFinalize(true);
  74. AddRef();
  75. _ownsHandle = true;
  76. }
  77. }
  78. private static readonly SendOrPostCallback _disposeDelegate = DisposeOnContext;
  79. private static void DisposeOnContext(object state)
  80. {
  81. (state as MicroComProxyBase)?.Dispose(false);
  82. }
  83. ~MicroComProxyBase()
  84. {
  85. if(!_ownsHandle)
  86. return;
  87. if (_synchronizationContext == null)
  88. Dispose();
  89. else
  90. _synchronizationContext.Post(_disposeDelegate, this);
  91. }
  92. }
  93. }