Files
docker-wine-dotnet/VisualStudioMock/ComProxy.cs
2025-02-04 00:55:10 +01:00

102 lines
3.4 KiB
C#

using System;
using System.Runtime.InteropServices;
namespace VisualStudioMock
{
delegate int GetInterfaceDelegate(IntPtr @this, [MarshalAs(UnmanagedType.LPStruct)] Guid iid, out IntPtr ppv);
delegate int AddRefDelegate(IntPtr @this);
delegate int ReleaseDelegate(IntPtr @this);
sealed class ComProxy
{
private const int S_OK = 0;
private const int E_NOINTERFACE = -2147467262;
private static readonly Guid IID_IUnknown = new Guid("00000000-0000-0000-c000-000000000046");
private static readonly Guid IID_IManagedObject = new Guid("C3FCC19E-A970-11D2-8B5A-00A0C9B7C9C4");
private readonly object _realInstance;
private readonly IntPtr _realInstanceN;
private readonly IntPtr _proxyInstanceN;
private readonly GetInterfaceDelegate _getInterface;
private readonly IntPtr _getInterfaceN;
private readonly AddRefDelegate _addRef;
private readonly ReleaseDelegate _release;
internal ComProxy(object instance)
{
this._realInstance = instance;
this._realInstanceN = Marshal.GetIUnknownForObject(instance);
this._proxyInstanceN = Marshal.AllocHGlobal(IntPtr.Size * 4);
IntPtr vtbl = _proxyInstanceN + IntPtr.Size;
Marshal.WriteIntPtr(_proxyInstanceN, vtbl);
Marshal.WriteIntPtr(vtbl, this._getInterfaceN = Marshal.GetFunctionPointerForDelegate((Delegate)(this._getInterface = new GetInterfaceDelegate(GetInterface))));
Marshal.WriteIntPtr(vtbl, IntPtr.Size * 1, Marshal.GetFunctionPointerForDelegate((Delegate)(this._addRef = new AddRefDelegate(AddRef))));
Marshal.WriteIntPtr(vtbl, IntPtr.Size * 2, Marshal.GetFunctionPointerForDelegate((Delegate)(this._release = new ReleaseDelegate(Release))));
}
internal object GetIUnknown()
{
return Marshal.GetObjectForIUnknown(_proxyInstanceN);
}
private int GetInterface(IntPtr @this, [MarshalAs(UnmanagedType.LPStruct)] Guid iid, out IntPtr ppv)
{
if (iid == IID_IUnknown)
{
ppv = _proxyInstanceN;
Ext.LogCall(_realInstance.GetType().Name, nameof(GetInterface), "IUnknown OK");
return S_OK;
}
if (iid == IID_IManagedObject)
{
ppv = IntPtr.Zero;
Ext.LogCall(_realInstance.GetType().Name, nameof(GetInterface), "IManagedObject Fail");
return E_NOINTERFACE;
}
int result = Marshal.QueryInterface(_realInstanceN, ref iid, out ppv);
if (result == S_OK)
{
// Patch the vtable with our own thunks
IntPtr origVtbl = Marshal.ReadIntPtr(ppv);
Type iType = null;
foreach (Type i in _realInstance.GetType().GetInterfaces())
if (i.GUID == iid)
{
iType = i;
break;
}
if (iType != null)
{
// TODO generate proper thunks in machine code
int slotCount = Marshal.GetEndComSlot(iType) + 1;
int slotsSizeBytes = IntPtr.Size * slotCount;
IntPtr newVtbl = Marshal.AllocHGlobal(slotsSizeBytes);
...
}
Ext.LogCall(_realInstance.GetType().Name, nameof(GetInterface), (iType == null ? iid.ToString() : iType.Name) + " OK");
}
else
{
Ext.LogCall(_realInstance.GetType().Name, nameof(GetInterface), iid.ToString() + " Fail");
}
return result;
}
private int AddRef(IntPtr @this)
{
return 1;
}
private int Release(IntPtr @this)
{
return 1;
}
}
}