无法从自定义域中解开 COM 实例

Posted

技术标签:

【中文标题】无法从自定义域中解开 COM 实例【英文标题】:Impossible to unwrap COM instance from custom domain 【发布时间】:2018-10-11 12:19:46 【问题描述】:

我正在尝试在自定义域的 COM 组件中进行调用。 问题是当我尝试打开我的 ObjectHandle 时,会抛出序列化异常。

但是如果我使用当前的 AppDomain 来创建实例,它就可以工作了......

异常信息:

An unhandled exception of type 'System.Runtime.Serialization.SerializationException' occurred in ConsoleApp1.exe
Additional information: Type 'MyAddin.Main' in assembly 'MyAddin, Version=2019.0.1.5, Culture=neutral, PublicKeyToken=null' is not marked as serializable.

还有我的代码:

using System;

namespace ConsoleApp1

    class Program
    

        static void Main(string[] args)
        
            string addinPath = "C:\\sources\\MyAddin\\bin\\x64\\Debug\\MyAddin.dll";
            string addinFolder = "C:\\sources\\MyAddin\\bin\\x64\\Debug\\";
            string addinConfigPath = "C:\\sources\\MyAddin\\bin\\x64\\Debug\\MyAddin.dll.config";

            System.AppDomainSetup setup = new System.AppDomainSetup();
            setup.ApplicationBase = addinFolder;
            setup.ConfigurationFile = addinConfigPath;
            setup.ApplicationName = "MyAddin.dll";

            string strClsid = "2616ad89-f4d1-4dc7-9d9d-a5de101b9085"; // CLSID of my COM addin

            System.AppDomain customDomain = System.AppDomain.CreateDomain(strClsid,  null,  setup);
            // The type of domain is System.Runtime.Remoting.Proxies.__TransparentProxy

            // The type of custom domain is 
            System.Type addinComType = System.Type.GetTypeFromCLSID(System.Guid.Parse(strClsid));

            System.Runtime.Remoting.ObjectHandle addinInstanceObjectHandle = customDomain.CreateComInstanceFrom(addinPath, addinComType.FullName); 
            System.Object addinInstance = addinInstanceObjectHandle.Unwrap(); //Throw the Serialization exception when create COM instance from customDomain
            // But working if I did System.AppDomain.CurrentDomain.CreateComInstanceFrom

            System.Reflection.MethodBase myMethod = addinInstance.GetType().GetMethod("connectToEwAPI");
            System.Object[] parameters =  null ;
            myMethod.Invoke(addinInstance, parameters);

        
    

所以我做了一些愚蠢的事情? 你知道我错过了什么吗?

提前感谢您的帮助


编辑

我尝试过使用更基本的程序集。 所以我有一个只有这段代码的 C# 程序集

namespace ClassLibrary2

    public class Class1
    
        public Class1()
        

        
        public string MyMethod() => "OK";
    

而我的 exe 代码只是

static void Main(string[] args)

    string basePath = @"C:\source\MyAddin\ConsoleApp1\ClassLibrary2\bin\Debug";
    string dllName = @"ClassLibrary2";
    string typeName = "ClassLibrary2.Class1";
    string dllFullpath = $"basePath\\dllName.dll";

    try
    
        ObjectHandle objectHandle = AppDomain.CurrentDomain.CreateInstanceFrom(dllFullpath, typeName);
        Object addinObject = objectHandle.Unwrap();
        var myAddinMethod = addinObject.GetType().GetMethod("MyMethod");
        string result = myAddinMethod.Invoke(addinObject, null) as string; // Working


        AppDomainSetup setup = new AppDomainSetup()
        
            ApplicationBase = basePath,
            ApplicationName = dllName,
            ConfigurationFile = dllName + ".dll.config",
            PrivateBinPath = basePath
        ;

        AppDomain customDomain = AppDomain.CreateDomain("MyDomain", null, setup);
        ObjectHandle objectHandleFromCustomDomain = customDomain.CreateInstanceFrom(dllFullpath, typeName);
        Object addinObjectFromCustomDomain = objectHandleFromCustomDomain.Unwrap();  // Exception thrown
        var myAddinMethodFromCustomDomain = addinObjectFromCustomDomain.GetType().GetMethod("MyMethod");
        string resultFromCustomDomain = myAddinMethodFromCustomDomain.Invoke(myAddinMethodFromCustomDomain, null) as string;
    
    catch(Exception e)
    
        var t = e.Message; // Exception thrown: 'System.Runtime.Serialization.SerializationException' by objectHandleFromCustomDomain.Unwrap()
    

如您所见,它适用于默认域,但不适用于自定义域....

你有什么想法吗?

【问题讨论】:

使用托管代码中的 [ComVisible] .NET 类或接口是一个非常糟糕的主意。 IDE 和类型库导入器会非常努力地阻止您这样做。但是他们可以被打败,当你使用后期绑定时没有保护。 AppDomain 细节只是可能出错的一件事。没有意义,只需添加对该程序集的引用即可。 首先感谢您的回答。事实上,这只是我的问题的一个提取(使问题更容易理解)。在“真实”中,此代码在本地 C++ 应用程序的 C++ 托管调用中。我尝试制作一个插件加载器,它将每个插件加载到自定义域中(在插件之间添加一些限制)。每个插件都可以随时添加或删除,所以我不能使用静态链接。 @HansPassant 尝试过不使用 COM,完整的 C# 解决方案,没有任何依赖,但仍然无法正常工作......你知道我想念什么吗? 你为什么要把每个插件都放在它自己的appdomain中?你真正想解决什么问题? @Ben 我正在尝试隔离插件,原因有两个。第一个是允许每个插件有自己的配置文件(特别是它自己的绑定重定向)。 2.不允许插件A破坏插件B中的某些东西。并且每个插件必须在主应用程序的同一进程中(所以我不能在另一个进程中生成插件) 【参考方案1】:

如果您想跨AppDomain 边界访问对象,您需要做以下两件事之一:

创建类Serializable,例如通过添加[Serializable] 属性。在这种情况下,对象的副本被序列化并通过AppDomain 边界。您调用的任何成员都将在副本上,并且不会影响原始对象。

使类派生自MarshalByRefObject。在这种情况下,对对象的引用跨 AppDomain 边界进行编组,您调用的任何成员都会影响其原始 AppDomain 中的对象。

【讨论】:

它是一个COM接口,所以我认为它已经可以序列化了。 (COM 的东西不能序列化???)我不能从 MarshalByRefObject 派生,因为我已经从我的 COM 接口派生了。 @David, ...because I already derive from my COM interface - 为什么不呢?您不能从 >1 基类派生,但可以从任意数量的接口派生。您的示例类 Class 不是从任何东西派生的;您是否尝试过将其从 MarshalByRefObject 派生? 对不起,我的错误,我确实明白你所说的。所以我尝试了[Serializable]MarshalByRefObject 并且仍然有相同的序列化异常......``` [ComVisible(true)] [Guid("16c8ab2e-04b2-456-ade1-f4f69c213393")] [可序列化] 公共类 Class1 : MarshalByRefObject, MyAPI.IAddIn ``` @David,您的类应该继承自 MarshalByRefObject,或者是可序列化的(这意味着它将按值编组),但不能两者兼而有之。在您的情况下,MarshalByRefObject 是合适的。在这种情况下,确切的异常消息是什么? 消息还是一样System.Runtime.Serialization.SerializationException: Type 'ClassLibrary2.Class1' in assembly 'ClassLibrary2, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' is not marked as serializable. à System.Runtime.Remoting.ObjectHandle.Unwrap() à ConsoleApp1.Program.Main(String[] args) dans C:\ConsoleApp1\ConsoleApp1\Program.cs:ligne 39

以上是关于无法从自定义域中解开 COM 实例的主要内容,如果未能解决你的问题,请参考以下文章

无法 RDP 或 ping 从自定义映像创建的 GCE 实例

无法从自定义查询中获取对象数据 Back& - Backand.com - 未定义

用于访问无法从自定义域访问的 S3 私有内容的签名 URL

WordPress - 无法从自定义帖子类型中的元框获取价值

无法从自定义表格视图单元格按钮中删除行

无法从自定义 UIView 调用按钮的方法