Nis*_*mar 8 .net c# com c++-cli com-interop
我在c ++中有基础库,客户端应用程序在C#中.有一个c ++/cli接口可以从C#访问c ++ api.一切都运行正常,直到多个应用程序域没有像NUnit或WCF托管一样发挥,即有一个应用程序域.
我已经在cli 中的gcroot中存储了托管对象以进行回调.我已经读过这是应用程序域问题的根本原因("无法通过AppDomains传递GCHandle")因为它们没有应用程序域信息(http://lambert.geek.nz/2007/05/29/unmanaged -appdomain-callback /).有人建议使用委托,但我的基础c ++层期望对象不是函数指针(http://www.lenholgate.com/blog/2009/07/error-cannot-pass-a-gchandle-across-appdomains.html).我也尝试过IntPtr,但在这种情况下,我无法在回调期间将其强制转换为我的托管对象.
UPDATE
让我再详细说明一下我的问题.
我在C#中有"Receiver"类,它作为输入参数传递给api之一.此接收器对象用于回调.在C++/CLI中,我创建了一个Native/unmanaged类"ObjectBinder",它与托管的Receiver类具有相同的副本(具有相同的方法).它保存了gcroot中托管接收者对象的参考.当我们从C#调用api时,它来到CLI层,app域是"client exe".我们在gcroot中的ObjectBinder中存储参数"managed receiver object",并将本机ObjectBinder对象的引用传递给C++.现在后端代码(c ++和c)将一个asyn回调(新线程)发送到c ++层,该层使用ObjectBinder对象向CLI发送回调用.现在我们在ObjectBinder对象的CLI层中.但是App域已被更改(在WCF或NUNIT或任何其他创建它自己的App域的服务的情况下,在编译时不知道).现在我想访问存储在gcroot中的托管Receiver对象,以便将回调发送回C#,但它给出了APP DOMAIN错误.
我也尝试过IntPtr和IUnknown*而不是gcroot与Marshal :: GetIUnknownForObject和Marshal :: GetObjectForIUnknown但是得到相同的错误.
您不能简单地使用GCHandle.ToIntPtr/ 编组.NET应用程序域之间的托管对象GCHandle.FromIntPtr,即使您派生自MarshalByRefObject或ContextBoundObject.
一种选择是使用COM和全局接口表(GIT).COM Marshaller和.NET运行时将一起编组调用,但您需要坚持使用托管对象实现的COM接口.这适用于跨不同domians和不同COM公寓线程的调用.
另一种选择是创建一个COM可调用包装器(CCW)Marshal.GetIUnknownForObject,然后Marshal.GetObjectForIUnknown从另一个域使用.如果您派生了托管代理对象,则会返回托管代理对象MarshalByRefObject,否则将返回非托管RCW代理.如果您在同一个线程上调用托管对象(虽然来自另一个应用程序域),这将有效.
这是一个例子,说明了原始问题(据我所知)和这两种可能的解决方案.我InterfaceIsIDispatch在这里使用一个后期绑定接口,以避免必须注册类型库(不需要做RegAsm,以防你除了跨域之外还要编组跨公寓).
using System;
using System.Runtime.InteropServices;
using System.Threading;
namespace ConsoleApplication
{
public class Program
{
[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)] // late binding only
public interface ITest
{
void Report(string step);
}
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
[ComDefaultInterface(typeof(ITest))]
public class ComObject: MarshalByRefObject, ITest
{
public void Report(string step)
{
Program.Report(step);
}
}
public static void Main(string[] args)
{
var obj = new ComObject();
obj.Report("Object created.");
System.AppDomain domain = System.AppDomain.CreateDomain("New domain");
// via GCHandle
var gcHandle = GCHandle.Alloc(obj);
domain.SetData("gcCookie", GCHandle.ToIntPtr(gcHandle));
// via COM GIT
var git = (ComExt.IGlobalInterfaceTable)(Activator.CreateInstance(Type.GetTypeFromCLSID(ComExt.CLSID_StdGlobalInterfaceTable)));
var comCookie = git.RegisterInterfaceInGlobal(obj, ComExt.IID_IUnknown);
domain.SetData("comCookie", comCookie);
// via COM CCW
var unkCookie = Marshal.GetIUnknownForObject(obj);
domain.SetData("unkCookie", unkCookie);
// invoke in another domain
domain.DoCallBack(() =>
{
Program.Report("Another domain");
// trying GCHandle - fails
var gcCookie2 = (IntPtr)(System.AppDomain.CurrentDomain.GetData("gcCookie"));
var gcHandle2 = GCHandle.FromIntPtr(gcCookie2);
try
{
var gcObj2 = (ComObject)(gcHandle2.Target);
gcObj2.Report("via GCHandle");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
// trying COM GIT - works
var comCookie2 = (uint)(System.AppDomain.CurrentDomain.GetData("comCookie"));
var git2 = (ComExt.IGlobalInterfaceTable)(Activator.CreateInstance(Type.GetTypeFromCLSID(ComExt.CLSID_StdGlobalInterfaceTable)));
var obj2 = (ITest)git2.GetInterfaceFromGlobal(comCookie2, ComExt.IID_IUnknown);
obj2.Report("via GIT");
// trying COM CCW
var unkCookie2 = (IntPtr)(System.AppDomain.CurrentDomain.GetData("unkCookie"));
// this casting works because we derived from MarshalByRefObject
var unkObj2 = (ComObject)Marshal.GetObjectForIUnknown(unkCookie2);
obj2.Report("via CCW");
});
Console.ReadLine();
}
static void Report(string step)
{
Console.WriteLine(new
{
step,
ctx = Thread.CurrentContext.GetHashCode(),
threadId = Thread.CurrentThread.ManagedThreadId,
domain = Thread.GetDomain().FriendlyName,
});
}
public static class ComExt
{
static public readonly Guid CLSID_StdGlobalInterfaceTable = new Guid("00000323-0000-0000-c000-000000000046");
static public readonly Guid IID_IUnknown = new Guid("00000000-0000-0000-C000-000000000046");
[ComImport(), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("00000146-0000-0000-C000-000000000046")]
public interface IGlobalInterfaceTable
{
uint RegisterInterfaceInGlobal(
[MarshalAs(UnmanagedType.IUnknown)] object pUnk,
[In, MarshalAs(UnmanagedType.LPStruct)] Guid riid);
void RevokeInterfaceFromGlobal(uint dwCookie);
[return: MarshalAs(UnmanagedType.IUnknown)]
object GetInterfaceFromGlobal(
uint dwCookie,
[In, MarshalAs(UnmanagedType.LPStruct)] Guid riid);
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
5014 次 |
| 最近记录: |