mirror of
https://gitee.com/csharpui/CPF.git
synced 2026-06-26 14:23:10 +08:00
初始化
This commit is contained in:
176
CPF.Windows/MicroCom/MicroComShadow.cs
Normal file
176
CPF.Windows/MicroCom/MicroComShadow.cs
Normal file
@@ -0,0 +1,176 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user