从辅助域调用主应用域中的方法并获取值

Posted

技术标签:

【中文标题】从辅助域调用主应用域中的方法并获取值【英文标题】:Calling methods and getting values in the main appdomain from a secondary domain 【发布时间】:2011-07-16 03:00:51 【问题描述】:

我正在为我的 C# IRC Bot 实现一个模块系统。这些模块是 .dll 程序集,它们存储在子目录“模块”中,它们用于向机器人添加功能,例如在 IRC 上添加额外的命令。这些模块设计为在运行时加载和卸载,因此我可以更新机器人或修复错误,而无需重新启动整个应用程序。

目前,模块系统为每个要加载的模块创建一个新的AppDomain,并在一个名为ModuleHelper 的类中使用CreateInstanceFromAndUnwrap 创建一个代理。

AppDomain domain = AppDomain.CreateDomain(name, null, new AppDomainSetup 
 
    ApplicationName = AppDomain.CurrentDomain.SetupInformation.ApplicationName,
    ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase,
    DisallowApplicationBaseProbing = true,
    PrivateBinPathProbe = ModuleDirectory,
    PrivateBinPath = ModuleDirectory,
    ShadowCopyDirectories = ModuleDirectory,
    CachePath = Path.Combine(ModuleDirectory, "cache"),
    ShadowCopyFiles = bool.TrueString 
);
ModuleProxy proxy = null;
try

    proxy = (ModuleProxy)domain.CreateInstanceFromAndUnwrap(location, AssemblyName.GetAssemblyName(location).Name + ".Module");
    proxy.OnLoad();

catch

    AppDomain.Unload(domain);
    throw;

此代理继承自 MarshalByRefObject

public abstract class ModuleProxy : MarshalByRefObject

    internal protected virtual void OnLoad()
    
    

    internal protected virtual void OnUnload()
    
    

OnLoadOnUnload 在模块加载或卸载时被调用。 模块也继承自外部程序集中的MarshalByRefObject,例如模块中的此类ConfigurationReader.dll

public class Module : ModuleProxy

    private Configuration _configuration = new Configuration();

    protected override void OnLoad()
    
        string fileName = Path.Combine(ModuleHelper.ModuleDirectory, AssemblyName.GetAssemblyName(Assembly.GetExecutingAssembly().Location).Name + ".conf");
        _configuration.ReadAndLoadConfiguration(fileName);
        IrcBot bot = new IrcBot(_configuration);
        if (_configuration.Perform != null)
        
            bot.EventManager.OnRegister += PerformOnRegister;
        
        if (!string.IsNullOrWhiteSpace(_configuration.IdentifyMatchPeer + _configuration.IdentifyMatchText + _configuration.IdentifyPassword))
        
            bot.EventManager.OnNotice += IdentifyOnNotice;
        
        IrcBot.Bots.Add(_configuration.Id, bot);
        IrcBot.Bots[_configuration.Id].Start();
    
...
...
...

问题是,当我修改属于主 appdomain 的内容时(特别是,将新机器人添加到 IrcBot.Bots 集合中,IrcBot.Bots.Add(_configuration.Id, bot);),IrcBot.Bots 计数仅在辅助 appdomain 内增加,而不是我想要的主应用程序域。

在做了一些 Console.WriteLining 之后,我发现在辅助 appdomain 中调用 Add 之后调用 IrcBot.Bots.Count 返回 1,并在主 appdomain 中调用 OnLoad 之后再次调用它返回 0 . 这会造成灾难性的后果,并导致之后加载的其他模块发生故障。 在辅助 AppDomain 中更改后,如何在主 AppDomain 中更新机器人计数(除其他外)?

【问题讨论】:

我对自己的回答没有信心,所以我会将其作为评论发布:) 我认为您无法使用 MarshalByRefObject 更新静态状态,因为其中一个AppDomain 将对其他 AppDomain 不可见。新建某种包含共享状态的“上下文”对象以在主域和子域之间传递可能更容易,但这条路包含很多哀号和咬牙切齿。您可能会发现使用 WCF 命名管道以更流畅的方式进行通信更容易,请参阅***.com/questions/50153/… 【参考方案1】:

正如朱丽叶所说,AppDomain 确实是隔离的,因此“静态”变量对其他 AppDomain 不可见。一个解决方案可能是使用跨 AppDomain 单例,如 http://jonathan.dickinsons.co.za/blog/2010/11/cross-domain-singleton-in-c/ 和 http://www.dolittle.com/blogs/einar/archive/2007/05/18/cross-appdomain-singleton.aspx 中所述。

【讨论】:

以上是关于从辅助域调用主应用域中的方法并获取值的主要内容,如果未能解决你的问题,请参考以下文章

获取不同域中的iframe父名称[重复]

将 DLL 动态加载到单独的应用程序域中,然后卸载

从具有依赖关系的另一个文件夹加载另一个应用程序域中的程序集

SQL Server 主表主键自增量,如何获取主键呢?

从编码域中的 MPEG 文件中读取元数据

如何使用 Python 获取域中的所有链接?