从辅助域调用主应用程序域中的方法并获取值
我正在为我的 C# IRC 机器人实现一个模块系统。这些模块是 .dll 程序集,存储在子目录“modules”中,它们用于向机器人添加功能,例如在 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()
{
}
}
OnLoad
和 OnUnload
在模块加载或卸载时被调用。 模块还继承自外部程序集中的 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();
}
...
...
...
问题是,当我修改属于主应用程序域的内容时(具体来说,将新机器人添加到 IrcBot.Bots 集合中,IrcBot.Bots.Add(_configuration.Id, bot);
) IrcBot.Bots
计数仅在辅助应用程序域内增加,而不是像我希望的那样在主应用程序域内增加。
做了一些 Console.WriteLining 后,我发现在辅助应用程序域中的 Add
调用之后调用 IrcBot.Bots.Count
返回 1,并在之后立即再次调用它主应用程序域中的 OnLoad
调用返回 0。这会产生灾难性的影响,并导致随后加载的其他模块出现故障。 在辅助 AppDomain 中更改后,如何更新主 AppDomain 中的机器人计数(以及其他内容)?
I am implementing a module system for my C# IRC Bot. The modules are .dll assemblies which are stored in a subdirectory, "modules", and they are used to add functionality to the bot, such as adding extra commands on IRC. These modules are designed to be loaded and unloaded at runtime so I can update the bot or fix bugs, without having to restart the entire application.
Currently the module system creates a new AppDomain
for each module to be loaded, and a proxy to be created using CreateInstanceFromAndUnwrap
inside a class called ModuleHelper
.
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;
}
This proxy inherits from MarshalByRefObject
.
public abstract class ModuleProxy : MarshalByRefObject
{
internal protected virtual void OnLoad()
{
}
internal protected virtual void OnUnload()
{
}
}
OnLoad
and OnUnload
are called when the module is loaded or unloaded.
Modules also inherit from MarshalByRefObject
in the external assembly, such as this class in a module, 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();
}
...
...
...
The problem is, when I modify something that belongs in the main appdomain (specifically, adding a new bot to the IrcBot.Bots collection, IrcBot.Bots.Add(_configuration.Id, bot);
) the IrcBot.Bots
count is increased inside the secondary appdomain only, and not the main appdomain as I want it to be.
After doing a bit of Console.WriteLining, I have found that calling IrcBot.Bots.Count
after the Add
call in the secondary appdomain returns 1, and calling it again straight after the OnLoad
call in the main appdomain returns 0. This has a disastrous effect, and causes the other modules that are loaded afterwards to malfunction. How can I update the bot count (among other things) in the main AppDomain after changing it in the secondary AppDomain?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
正如 Juliet 所说,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。
As Juliet said, AppDomains are indeed isolated so that "static" variables are not visible from other AppDomains. A solution might be using cross-AppDomain singletons, as explained at http://jonathan.dickinsons.co.za/blog/2010/11/cross-domain-singleton-in-c/ and http://www.dolittle.com/blogs/einar/archive/2007/05/18/cross-appdomain-singleton.aspx.