【问题标题】:Mono can't cast TypeA to TypeAMono 无法将 Type 转换为 Type A
【发布时间】:2013-11-03 10:08:20
【问题描述】:

我正在用 C# 创建一个插件框架。框架的主要要求是在运行时加载、卸载和更新插件。

为了实现这一点,我一直在创建 AppDomain 并将插件程序集加载到 AppDomain 中。

在 Windows 上的 Microsoft .NET 上一切正常,但插件不适用于在 mac 或 linux 上运行的单声道。

当尝试启动插件时,我收到如下异常:

不能转换类型为 'System.Func`1[[API.Network.NodeType, API, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]] 的参数 0, mscorlib, Version=4.0.0.0, Culture =neutral,PublicKeyToken=b77a5c561934e089' 输入 'System.Func`1[[API.Network.NodeType,API,Version=1.0.0.0,Culture=neutral,PublicKeyToken=null]],mscorlib,Version=4.0.0.0,Culture =中性,PublicKeyToken=b77a5c561934e089'

这是因为每个插件都有自己的 API.dll 程序集副本,尽管该程序集是相同的副本,但 mono 并不认为类型是相同的。

如何让插件从主应用程序目录加载 API.dll?或者,或者,我怎样才能让 mono 看到类型相同?

【问题讨论】:

    标签: c# linux macos plugins mono


    【解决方案1】:

    为了找到您问题的答案,我创建了一个简单的插件系统,并在 Windows 下的 mono 3.2.3 上成功测试了它(不幸的是,我现在无法在 Linux 上进行测试,也许明天)。我的代码:

    SDK.dll

    using System;
    
    namespace SDK
    {
        public interface IPlugin
        {
            void SomeMethod();
    
            SomeSDKType GetSDKType();
    
        }
    }
    
    using System;
    using System.Collections.Generic;
    
    namespace SDK
    {
        [Serializable]
        public class StringEventArgs : EventArgs
        {
    
            public string Message { get; set; }
    
        }
    
        public class SomeSDKType : MarshalByRefObject
        {
    
            public event EventHandler<StringEventArgs> SDKEvent;
    
            public Action SDKDelegate;
    
            public void RiseSDKEvent(string message)
            {
                var handler = SDKEvent;
                if (handler != null) SDKEvent(this, new StringEventArgs { Message = message });
            }
    
            public Dictionary<int, string> GetDictionary()
            {
                var dict = new Dictionary<int, string> ();
                dict.Add(1, "One");
                dict.Add(2, "Two");
                return dict;
            }
    
        }
    }
    

    插件.dll

    using System;
    using SDK;
    
    namespace Plugin
    {
        public class Plugin : MarshalByRefObject, IPlugin
        {
            public Plugin()
            {
            }
    
            public void SomeMethod()
            {
                Console.WriteLine("SomeMethod");
            }
    
            public SomeSDKType GetSDKType()
            {
                var obj = new SomeSDKType();
                obj.SDKDelegate = () => Console.WriteLine("Delegate called from {0}", AppDomain.CurrentDomain.FriendlyName);
                return obj;
            }
        }
    }
    

    托管计划

    using System;
    using System.Reflection;
    using System.IO;
    using SDK;
    
    namespace AppDomains
    {
        class MainClass
        {
            public static void Main(string[] args)
            {
                var domain = AppDomain.CreateDomain("Plugin domain"); // Domain for plugins
                domain.Load(typeof(IPlugin).Assembly.FullName); // Load assembly containing plugin interface to domain 
    
                var currentPath = Directory.GetCurrentDirectory();
                var pluginPath = Path.Combine(currentPath, "Plugins");
                var pluginFiles = Directory.GetFiles(pluginPath, "*.dll");
                foreach (var pluginFile in pluginFiles) // Foreach dll in Plugins directory
                {
                    var asm = Assembly.LoadFrom(pluginFile);
                    foreach (var exportedType in asm.GetExportedTypes())
                    {
                        if (!typeof(IPlugin).IsAssignableFrom(exportedType)) continue; // Check if exportedType implement IPlugin interface
                        domain.Load(asm.FullName); // If so load this dll into domain
                        var plugin = (IPlugin)domain.CreateInstanceAndUnwrap(asm.FullName, exportedType.FullName); // Create plugin instance
                        plugin.SomeMethod(); // Call plugins methods
                        var obj = plugin.GetSDKType();
                        obj.SDKDelegate();
                        var dict = obj.GetDictionary();
                        foreach (var pair in dict)
                        {
                            Console.WriteLine("{0} - {1}", pair.Key, pair.Value);
                        }
                        obj.SDKEvent += obj_SDKEvent;
                        obj.RiseSDKEvent(string.Format("Argument from domain {0}", AppDomain.CurrentDomain.FriendlyName));
                    }
                }
                Console.ReadLine();
            }
    
            static void obj_SDKEvent(object sender, StringEventArgs e)
            {
                Console.WriteLine("Received event in {0}", AppDomain.CurrentDomain.FriendlyName);
                Console.WriteLine(e.Message);
            }
        }
    }
    

    App.config

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <runtime>
        <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
             <probing privatePath="Plugins"/>
        </assemblyBinding>
      </runtime>
    </configuration>
    

    对代码的一些解释。我用插件接口创建了 SDK dll。所有插件和宿主应用程序都必须引用它。必须在没有 SDK dll 的情况下提供插件,因为主机应用程序已经包含它。它们放入宿主应用程序目录中的 Plugins 目录(即,如果应用路径 = c:\MyApp 插件位于 c:\MyApp\Plugins em>) 以便为 CLR(或单声道)提供查找插件程序集的机会,我还创建了带有探测元素的 App.config 文件。

    希望这会有所帮助。

    【讨论】:

    • 您的解决方案仍然无法正常工作,因为“domain.Load(typeof(IPlugin).Assembly.FullName);”仍将加载 SDK.dll 的插件副本而不是主机应用程序的副本(FullName 是程序集名称而不是文件名)。我当前的代码已经可以创建插件的实例。尝试使用需要在 AppDomain 之间传递的 API(您的 SDK.dll)中定义的类型时会出现问题。
    • 这很奇怪。在我的示例中,我只有一个 SDK.dll 副本(我在应用程序根目录中有 AppDomains.exe 和 SDK.dll 程序集,在应用程序 root\Plugins 中有 Plugin.dll)。当您使用 domain.Load() 每个域加载它自己的程序集副本并在 GAC 和应用程序目录中按全名搜索它(仅在域之间共享 mscorelib.dll)。因此,在我的示例中,应用程序域和插件域加载相同的 SDK.dll。
    • 我更新了答案以提供更复杂的示例。使用跨域交互时可能会出现一些问题。首先确保插件引用的所有程序集都位于 GAC 或应用程序目录中。其次,您跨应用程序域的所有对象都必须从 MarshalByRefObject 派生(或标记为 [Serializable])。您也不能使用 domain.CreateInstanceAndUnwrap() 创建委托和数组的实例。
    • 啊,我看到您提倡将 appdomain 设置为从主机目录加载它的所有引用。这在一定程度上有效,但当插件有引用时会出现问题:/ 影子复制任何依赖项都会很快变得非常混乱。
    • 顺便问一下,你为什么不使用microsoft MEF?单声道不行吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-09-24
    • 1970-01-01
    • 2020-08-14
    • 2012-04-25
    • 2020-07-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多