首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >在没有委托的情况下,GCHandle不能跨AppDomains:解决方案传递?

在没有委托的情况下,GCHandle不能跨AppDomains:解决方案传递?
EN

Stack Overflow用户
提问于 2014-06-09 10:12:53
回答 2查看 7.1K关注 0票数 9

我在c++中有基本库,客户端应用程序在C#中。有c++/cli接口从c++访问C# api。每件事情都可以正常工作,直到不止一个应用程序域没有像NUnit或WCF托管那样发挥作用,即一个应用程序域。

我已经将托管对象存储在cli中的gcroot中以进行回调。我读到过,这是导致应用程序域问题的根本原因(“不能通过GCHandle通过AppDomains"),因为它们没有应用域信息(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,但在本例中,我无法在回调期间将其转换为托管对象。

更新

让我再详细阐述一下我的问题。

我在中有类,并将其作为输入参数传递给其中一个api。此接收器对象用于回调。在C++/CLI中,我创建了一个原生"ObjectBinder"类,它是托管接收类的相同副本(具有相同的方法)。它在gcroot中具有托管接收对象的引用。当我们从C#调用该api时,就会涉及到CLI层,而应用程序域是"client“。我们将参数“托管接收对象”存储在ObjectBinder中,并将本地ObjectBinder对象的引用传递给C++。现在,后端代码(c++和c)将asyn回调(新线程)发送到c++层,该层使用ObjectBinder对象向CLI发送回调用。现在我们在ObjectBinder对象的CLI层中。,但应用程序域已被更改(如果是WCF或NUNIT或任何其他创建自己的App域(编译时不知道)的服务),。现在,我想访问托管接收对象,它存储在gcroot中,以便将回调发送回C#,但它导致了应用程序域错误。

我还尝试了Marshal::GetIUnknownForObjectMarshal::GetObjectForIUnknownIntPtrIUnknown *,而不是gcroot,但是也出现了相同的错误。

EN

回答 2

Stack Overflow用户

发布于 2014-06-09 13:14:23

您不能简单地使用.NET /GCHandle.FromIntPtrGCHandle.ToIntPtr应用程序域之间封送托管对象,即使您是从MarshalByRefObjectContextBoundObject派生的。

这样做的一个选择是使用COM和全局接口表(GIT)。COM Marshaller和.NET运行时将把调用编组在一起,但是您需要坚持使用由托管对象实现的COM接口。这将适用于跨不同域名和不同COM公寓线程的调用。

另一种选择是使用Marshal.GetIUnknownForObject创建COM可调用包装器(CCW),然后从另一个域使用Marshal.GetObjectForIUnknown。如果您是从MarshalByRefObject派生的,那么您将得到一个托管代理对象,否则将得到一个非托管RCW代理。如果您在同一个线程上调用您的托管对象(尽管来自另一个应用程序域),这将有效。

下面是一个示例,说明了最初的问题(据我理解)以及这两种可能的解决方案。我在这里使用了一个后期绑定的InterfaceIsIDispatch接口,以避免注册类型库(除了跨域之外,您也不需要做RegAsm,以防您也想封送跨公寓)。

代码语言:javascript
复制
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);
            }
        }
    }
}
票数 8
EN

Stack Overflow用户

发布于 2016-11-27 10:32:23

解决此问题的一个可能方法是从您的CrossAppDomainSingleton类调用ObjectBinder类。CrossAppDomainSingleton可以保存对接收方实例的引用。这个解决方案将把您的调用发送到一个专用的应用程序域。

如果您有多个接收实例,则仍然可以使用单例中的映射逻辑,并在回调中传递某种id。

您可以找到一个实现这里

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/24118047

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档