mirror of
https://gitee.com/csharpui/CPF.git
synced 2025-04-05 17:37:51 +08:00
177 lines
5.4 KiB
C#
177 lines
5.4 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text;
|
|
using System.Threading;
|
|
|
|
namespace CPF.Windows.MicroCom
|
|
{
|
|
public unsafe class MicroComShadow : IDisposable
|
|
{
|
|
private readonly object _lock = new object();
|
|
private readonly Dictionary<Type, IntPtr> _shadows = new Dictionary<Type, IntPtr>();
|
|
private readonly Dictionary<IntPtr, Type> _backShadows = new Dictionary<IntPtr, Type>();
|
|
private GCHandle? _handle;
|
|
private volatile int _refCount;
|
|
internal IMicroComShadowContainer Target { get; }
|
|
internal MicroComShadow(IMicroComShadowContainer target)
|
|
{
|
|
Target = target;
|
|
Target.Shadow = this;
|
|
}
|
|
|
|
internal int QueryInterface(Ccw* ccw, Guid* guid, void** ppv)
|
|
{
|
|
if (MicroComRuntime.TryGetTypeForGuid(*guid, out var type))
|
|
return QueryInterface(type, ppv);
|
|
else
|
|
return unchecked((int)0x80004002u);
|
|
}
|
|
|
|
internal int QueryInterface(Type type, void** ppv)
|
|
{
|
|
if (!type.IsInstanceOfType(Target))
|
|
return unchecked((int)0x80004002u);
|
|
|
|
var rv = GetOrCreateNativePointer(type, ppv);
|
|
if (rv == 0)
|
|
AddRef((Ccw*)*ppv);
|
|
return rv;
|
|
}
|
|
|
|
internal int GetOrCreateNativePointer(Type type, void** ppv)
|
|
{
|
|
if (!MicroComRuntime.GetVtableFor(type, out var vtable))
|
|
return unchecked((int)0x80004002u);
|
|
lock (_lock)
|
|
{
|
|
|
|
if (_shadows.TryGetValue(type, out var shadow))
|
|
{
|
|
var targetCcw = (Ccw*)shadow;
|
|
AddRef(targetCcw);
|
|
*ppv = targetCcw;
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
var intPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Ccw)));
|
|
var targetCcw = (Ccw*)intPtr;
|
|
*targetCcw = default;
|
|
targetCcw->RefCount = 0;
|
|
targetCcw->VTable = vtable;
|
|
if (_handle == null)
|
|
_handle = GCHandle.Alloc(this);
|
|
targetCcw->GcShadowHandle = GCHandle.ToIntPtr(_handle.Value);
|
|
_shadows[type] = intPtr;
|
|
_backShadows[intPtr] = type;
|
|
*ppv = targetCcw;
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
internal int AddRef(Ccw* ccw)
|
|
{
|
|
if (Interlocked.Increment(ref _refCount) == 1)
|
|
{
|
|
try
|
|
{
|
|
Target.OnReferencedFromNative();
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
MicroComRuntime.UnhandledException(Target, e);
|
|
}
|
|
}
|
|
|
|
return Interlocked.Increment(ref ccw->RefCount);
|
|
}
|
|
|
|
internal int Release(Ccw* ccw)
|
|
{
|
|
Interlocked.Decrement(ref _refCount);
|
|
var cnt = Interlocked.Decrement(ref ccw->RefCount);
|
|
if (cnt == 0)
|
|
return FreeCcw(ccw);
|
|
|
|
return cnt;
|
|
}
|
|
|
|
int FreeCcw(Ccw* ccw)
|
|
{
|
|
lock (_lock)
|
|
{
|
|
// Shadow got resurrected by a call to QueryInterface from another thread
|
|
if (ccw->RefCount != 0)
|
|
return ccw->RefCount;
|
|
|
|
var intPtr = new IntPtr(ccw);
|
|
var type = _backShadows[intPtr];
|
|
_backShadows.Remove(intPtr);
|
|
_shadows.Remove(type);
|
|
Marshal.FreeHGlobal(intPtr);
|
|
if (_shadows.Count == 0)
|
|
{
|
|
_handle?.Free();
|
|
_handle = null;
|
|
try
|
|
{
|
|
Target.OnUnreferencedFromNative();
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
MicroComRuntime.UnhandledException(Target, e);
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
Needs to be called to support the following scenario:
|
|
1) Object created
|
|
2) Object passed to native code, shadow is created, CCW is created
|
|
3) Native side has never called AddRef
|
|
|
|
In that case the GC handle to the shadow object is still alive
|
|
*/
|
|
|
|
public void Dispose()
|
|
{
|
|
lock (_lock)
|
|
{
|
|
List<IntPtr> toRemove = null;
|
|
foreach (var kv in _backShadows)
|
|
{
|
|
var ccw = (Ccw*)kv.Key;
|
|
if (ccw->RefCount == 0)
|
|
{
|
|
if (toRemove == null)
|
|
{
|
|
toRemove = new List<IntPtr>();
|
|
}
|
|
toRemove.Add(kv.Key);
|
|
}
|
|
}
|
|
|
|
if (toRemove != null)
|
|
foreach (var intPtr in toRemove)
|
|
FreeCcw((Ccw*)intPtr);
|
|
}
|
|
}
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
struct Ccw
|
|
{
|
|
public IntPtr VTable;
|
|
public IntPtr GcShadowHandle;
|
|
public volatile int RefCount;
|
|
public MicroComShadow GetShadow() => (MicroComShadow)GCHandle.FromIntPtr(GcShadowHandle).Target;
|
|
}
|
|
}
|