在运行时加载,使用和卸载程序集

时间:2011-12-01 16:34:14

标签: vb.net memory-management marshalling appdomain .net-assembly

我正在开发一个项目,我必须在运行时加载程序集(让我们称之为任务),运行任务然后能够终止整个程序集,以便可以在不中断主应用程序的情况下替换dll。

这些任务中有许多在主应用程序中运行,有些是顺序运行的,有些是并行运行的。有时,需要更新其中一个或一个任务,然后重新添加到队列中。目前,我们正在停止整个应用程序,并在任何阶段中断其他任务,这是不理想的。

我已经发现我必须将每个程序集加载到一个单独的AppDomain中,但是在这些域中加载程序集很困难,尤其是当我需要实际运行任务并从中接收事件时。我一直在研究这个问题几天,但仍然没有设法得到一个有效的概念验证。

我有一个任务接口,包括'run'和'kill'方法(subs)和'taskstep','complete'和'killed'事件。 'taskstep'返回一个稍后要缓存的对象,'complete'在整个任务完成时触发,'killed'在准备好卸载时触发。在两个小时的整个过程中也应该有超时,并且在被杀死的事件上超时2分钟以防它被卡住,此时我希望能够卸载它,强制任何线程终止(这是什么'杀'应该做什么)。每个程序集可能包含多个任务,所有这些任务都应该是可卸载的。

我将这些任务加载为“插件”没有问题但是在尝试使用它们并卸载它们时丢失了。如果我必须创建一些精心设计的包装器那么它就是它,但我甚至可能需要它吗?

我尝试从MarshalByRefObject继承,但我甚至不知道程序集fullname,除非我先加载它,然后锁定文件。我已经尝试从程序集的字节数组加载。这意味着该文件未被锁定,但其副本仍保留在当前的appdomain中。这将在接下来的几个月/几年内成为问题!

For Each key As String In assemblies.Keys

    Dim ad As AppDomain = AppDomainHelper.BuildChildAppDomain(AppDomain.CurrentDomain, key)

    AddHandler ad.AssemblyResolve, AddressOf AssemblyResolve

    _l.Add(ad)

    For Each value As String In assemblies(key)
        Dim item As IScanner = CType(ad.CreateInstanceAndUnwrap(key, value), IScanner)
        ListBox1.Items.Add(item)
    Next
Next

Private Function AssemblyResolve(sender As Object, args As ResolveEventArgs) As Assembly
    Return GetType(IScanner).Assembly
End Function

2 个答案:

答案 0 :(得分:1)

考虑使用Managed Extensibility Framework

它是.Net 4.0的一部分,您可以阅读简短的概述here

加载和卸载程序集可能是一个非常糟糕的主意 - 事情不会像您期望的那样工作(尝试捕获另一个AppDomain中抛出的异常?BAM你不能!)

答案 1 :(得分:0)

看来异常是从新的(辅助的,如果你愿意的话)AppDomain引发的。这意味着ResolveEventArgs必须序列化以跨越appdomain边界。您应该从辅助应用程序域中处理事件,这样您就不必越过该边界。

所以...创建一个处理分辨率的类......类似......

Public Class AssemblyResolver
  Inherits MarshalByRefObject

  Public Property SearchPath As String = String.Empty

  Public Function ResolveAssembly(sender as Object, e As ResolveEventArgs) As Assembly

    Dim tPath As String = Path.Combine(SearchPath, e.Name & ".dll")

    If File.Exists(tPath) Then
      Return Assembly.LoadFrom(tPath)
    End If

    Return Nothing

  End Function

End Class

现在从你的插件加载器中使用它......

Dim tPluginDomain As AppDomain = AppDomainHelper.BuildChildAppDomain(AppDomain.CurrentDomain, key)
Dim tResolver As AssemblyResolver = tPluginDomain.CreateInstanceAndUnwrap(GetType(AssemblyResolver).Assembly.FullName, GetType(AssemblyResolver).FullName)
AddHandler tPluginDomain.AssemblyResolve, AddressOf tResolver.ResolveAssembly

那应该让你指出正确的道路 参考MSDN,所有代码都是我的头脑。不知道它是否会编译..期望输入错误/调试它。

EDIT 哎呀..忘了将解析器标记到插件域中。固定的。