Goz*_*Goz 8 .net c# com casting com-interop
我有一对使用相同COM接口的库.在一个库中,我有一个实现该接口的类.另一个库需要一个实现接口的对象.
但是两个库都有自己的接口定义.两者略有不同,但基本上是相同的界面.
所以我尝试将它们放在如下:
Library2.Interface intf = (Library2.Interface)impl;
Run Code Online (Sandbox Code Playgroud)
但这引起了例外.如果我执行以下操作:
Library1.Interface intf = (Library1.Interface)impl;
Run Code Online (Sandbox Code Playgroud)
然后它没有问题,但我不能再将类传递给Library2.
我天真地假设两个具有相同GUID的接口都可以防止这是一个问题,但我似乎错了.有谁知道如何在两个库之间进行转换?也许是通过某种元帅?
这是一个非常有趣的问题,我想我可能会有一个有趣的解决方案.所以,虽然Library1.Interface和Library2.Interface是两个二进制兼容的ComImport接口,它们仍然是两个不同的.NET接口,并且不能被强制转换成对方.
为了使转换成为可能,我们需要以某种方式隐藏Library1.InterfaceCOM可调用包装器(CCW)后面的托管.NET对象的身份,然后确保此CCW不会被封送到同一个.NET对象.这样.NET编组器就会创建一个单独的RCW代理,然后可以将其转换Library2.Interface为普通的COM对象.
除了使用单独的COM公寓Library1.Interface和Library2.Interface对象之外,我只能想到另一种方法:COM聚合.任何.NET对象都可以Marshal.CreateAggregatedObject作为内部对象进行聚合.诀窍是构造非托管IUnknownCOM标识对象以用作聚合的外(父)对象.当从.NET访问时,这样的外部对象将被赋予单独的RCW代理.
以下是我对此的看法:
var server = ComWrapper.Create<Library2.Interface>(() => new Library1.Server());
var client = new Library2.Client();
client.CallMethod(server);
Run Code Online (Sandbox Code Playgroud)
作为控制台应用程序的整个逻辑(需要了解COM二进制协议的某些知识才能理解此代码):
using System;
using System.Runtime.InteropServices;
namespace Library1
{
[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("4C08A691-5D61-4E9A-B16D-75BAD2834BAE")]
public interface Interface
{
void TestMethod();
}
[ComVisible(true)]
public class Server : Interface
{
public Server() { }
public void TestMethod()
{
Console.WriteLine("TestMethod called");
}
}
}
namespace Library2
{
[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("4C08A691-5D61-4E9A-B16D-75BAD2834BAE")]
public interface Interface
{
void TestMethod();
}
public class Client
{
public void CallMethod(Library2.Interface server)
{
server.TestMethod();
}
}
}
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
// convert Library1.Server to Library2.Interface
var server = ComWrapper.Create<Library2.Interface>(() => new Library1.Server());
var client = new Library2.Client();
client.CallMethod(server);
Marshal.ReleaseComObject(server);
Console.ReadLine();
}
}
/// <summary>
/// ComWrapper - http://stackoverflow.com/q/26758316/1768303
/// by Noseratio
/// </summary>
public class ComWrapper
{
readonly Guid IID_IUnknown = new Guid("00000000-0000-0000-C000-000000000046");
const int S_OK = 0;
const int E_FAIL = unchecked((int)0x80004005);
delegate int QueryInterfaceMethod(IntPtr pUnk, ref Guid iid, out IntPtr ppv);
delegate int AddRefMethod(IntPtr pUnk);
delegate int ReleaseMethod(IntPtr pUnk);
[StructLayout(LayoutKind.Sequential)]
struct UnkObject
{
public IntPtr pVtable;
}
[StructLayout(LayoutKind.Sequential)]
struct UnkVtable
{
public IntPtr pQueryInterface;
public IntPtr pAddRef;
public IntPtr pRelease;
}
int _refCount = 0;
IntPtr _pVtable;
IntPtr _outerObject;
IntPtr _aggregatedObject;
GCHandle _gcHandle;
QueryInterfaceMethod _queryInterfaceMethod;
AddRefMethod _addRefMethod;
ReleaseMethod _releaseMethod;
private ComWrapper()
{
}
~ComWrapper()
{
Console.WriteLine("~ComWrapper");
Free();
}
private IntPtr Initialize(Func<object> createInnerObject)
{
try
{
// implement IUnknown methods
_queryInterfaceMethod = delegate(IntPtr pUnk, ref Guid iid, out IntPtr ppv)
{
lock (this)
{
// delegate anything but IID_IUnknown to the aggregated object
if (IID_IUnknown == iid)
{
ppv = _outerObject;
Marshal.AddRef(_outerObject);
return S_OK;
}
return Marshal.QueryInterface(_aggregatedObject, ref iid, out ppv);
}
};
_addRefMethod = delegate(IntPtr pUnk)
{
lock (this)
{
return ++_refCount;
}
};
_releaseMethod = delegate(IntPtr pUnk)
{
lock (this)
{
if (0 == --_refCount)
{
Free();
}
return _refCount;
}
};
// create the IUnknown vtable
var vtable = new UnkVtable();
vtable.pQueryInterface = Marshal.GetFunctionPointerForDelegate(_queryInterfaceMethod);
vtable.pAddRef = Marshal.GetFunctionPointerForDelegate(_addRefMethod);
vtable.pRelease = Marshal.GetFunctionPointerForDelegate(_releaseMethod);
_pVtable = Marshal.AllocCoTaskMem(Marshal.SizeOf(vtable));
Marshal.StructureToPtr(vtable, _pVtable, false);
// create the IUnknown object
var unkObject = new UnkObject();
unkObject.pVtable = _pVtable;
_outerObject = Marshal.AllocCoTaskMem(Marshal.SizeOf(unkObject));
Marshal.StructureToPtr(unkObject, _outerObject, false);
// pin the managed ComWrapper instance
_gcHandle = GCHandle.Alloc(this, GCHandleType.Normal);
// create and aggregate the inner object
_aggregatedObject = Marshal.CreateAggregatedObject(_outerObject, createInnerObject());
return _outerObject;
}
catch
{
Free();
throw;
}
}
private void Free()
{
Console.WriteLine("Free");
if (_aggregatedObject != IntPtr.Zero)
{
Marshal.Release(_aggregatedObject);
_aggregatedObject = IntPtr.Zero;
}
if (_pVtable != IntPtr.Zero)
{
Marshal.FreeCoTaskMem(_pVtable);
_pVtable = IntPtr.Zero;
}
if (_outerObject != IntPtr.Zero)
{
Marshal.FreeCoTaskMem(_outerObject);
_outerObject = IntPtr.Zero;
}
if (_gcHandle.IsAllocated)
{
_gcHandle.Free();
}
}
public static T Create<T>(Func<object> createInnerObject)
{
var wrapper = new ComWrapper();
var unk = wrapper.Initialize(createInnerObject);
Marshal.AddRef(unk);
try
{
var comObject = Marshal.GetObjectForIUnknown(unk);
return (T)comObject;
}
finally
{
Marshal.Release(unk);
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
536 次 |
| 最近记录: |