Vb.net 卸载DLL直到它';需要什么

Vb.net 卸载DLL直到它';需要什么,vb.net,dll,Vb.net,Dll,我在这里读到的关于使用AppDomains卸载插件DLL的一些答案让我很难受。以下是我的架构: 在我的解决方案中,我有一个SharedObjects项目,其中包含一个ModuleBase类,所有插件(解决方案中的独立项目)都继承该类。在SharedObjects项目中,我还有一个所有插件都可以实现的接口(因此,如果我有六个插件,它们都实现了相同的接口,因此使用这些插件的主程序在编译插件时不需要知道甚至不关心插件类的名称;它们都实现了相同的接口,因此公开了相同的信息)。每个插件项目都有一个对Sha

我在这里读到的关于使用AppDomains卸载插件DLL的一些答案让我很难受。以下是我的架构:

在我的解决方案中,我有一个
SharedObjects
项目,其中包含一个
ModuleBase
类,所有插件(解决方案中的独立项目)都继承该类。在
SharedObjects
项目中,我还有一个所有插件都可以实现的接口(因此,如果我有六个插件,它们都实现了相同的接口,因此使用这些插件的主程序在编译插件时不需要知道甚至不关心插件类的名称;它们都实现了相同的接口,因此公开了相同的信息)。每个插件项目都有一个对
SharedObjects
项目的项目引用。(作为旁注,可能很重要,也可能不重要,
SharedObjects
项目有一个对另一个解决方案的项目引用,
CompanyObjects
包含许多常用的类、类型、对象等。)当所有这些都完成后,当任何给定插件编译时,输出目录包含以下DLL:

  • 插件本身的编译DLL
  • SharedObjects
    项目中的DLL
  • 公司对象
    项目中的DLL
  • 公司对象
    项目中引用的四个必备第三方DLL
  • 我的主程序创建了一个对我正在做所有插件相关工作的类的引用(该类,
    pluginhelp
    ,存储在
    SharedObjects
    项目中)。该程序提供了一个OpenFileDialog,以便用户可以选择DLL文件。现在,由于它正在运行,我可以将插件DLL移动到单独的文件夹中,并使用
    Assembly.LoadFrom(PathToDLL)加载它们
    语句。它们加载时没有错误;我检查以确保它们在
    SharedObjects
    项目中实现了接口,收集了一些基本信息,并初始化了插件DLL本身的一些后台工作,以便接口可以公开。问题是,如果不退出主程序,我无法升级这些DLL因为我一使用
    LoadFrom
    这些DLL就被锁定了

    从中,我找到了锁定DLL问题的解决方案。但是,我使用为OP工作的代码得到了与OP相同的“未找到文件或依赖项”错误。当我从包含其余DLL的发布文件夹打开DLL时,我甚至得到了错误

    FusionLog甚至更令人困惑:没有提到我试图打开的路径;它试图查找我调试主程序的目录,这是一个与插件路径完全不同的项目,它查找的文件是DLL的名称,但在程序所在的文件夹中am正在运行。在这一点上,我不知道为什么它会忽略我给它的路径,并在一个完全不同的文件夹中查找DLL

    作为参考,这里是我的
    Loader
    类和我用来(尝试)加载DLL的代码:

    Private Class Loader
        Inherits MarshalByRefObject
    
        Private _assembly As [Assembly]
        Public ReadOnly Property TheAssembly As [Assembly]
            Get
                Return _assembly
            End Get
        End Property
    
        Public Overrides Function InitializeLifetimeService() As Object
            Return Nothing
        End Function
    
        Public Sub LoadAssembly(ByVal path As String)
            _assembly = Assembly.Load(AssemblyName.GetAssemblyName(path))
        End Sub
    
        Public Function GetAssembly(ByVal path As String) As Assembly
            Return Assembly.Load(AssemblyName.GetAssemblyName(path))    'this doesn't throw an error
        End Function
    End Class
    
    Public Sub Add2(ByVal PathToDll As String)
        Dim ad As AppDomain = AppDomain.CreateDomain("TempPluginDomain")
        Dim l As Loader = ad.CreateInstanceAndUnwrap(
            GetType(Loader).Assembly.FullName,
            GetType(Loader).FullName
        )
        Dim theDll As Assembly = l.GetAssembly(PathToDll)    'error happens here
        'there's another way to do it that makes the exact point of the error clear:
        'Dim theDll As Assembly = Nothing
        'l.LoadAssembly(PathToDll)    'No problems here. The _assembly variable is successfully set
        'theDll = l.TheAssembly       'Here's where the error occurs, as soon as you try to read that _assembly variable.
        AppDomain.Unload(ad)
    End Sub
    

    有谁能给我指出正确的方向,这样我就可以只根据需要加载和卸载DLL,而不会出现任何依赖性错误吗?

    我想我终于找到了它。结果是,我需要几个东西——我需要共享的DLL都放在一个地方,正如Hans上面提到的,我需要我的应用程序域。我的解决方案架构是这样的:一个折叠一个“共享对象”程序集,其中一个类文件用于基本插件体系结构,另一个类包含我的“插件包装器”类和支持类;以及将所有内容联系在一起的控制台应用程序。每个插件项目都有一个指向共享对象项目的项目引用,控制台应用程序也是如此。没有任何内容直接引用插件

    因此,在我的共享对象项目中,我有我的
    PluginBase
    类和我的
    IPlugin
    接口的代码:

    Public Interface IPlugin
        ReadOnly Property Result As Integer
        Sub Calculate(ByVal param1 As Integer, ByVal param2 As Integer)
    End Interface
    
    Public MustInherit Class PluginBase
        Inherits MarshalByRefObject
    
        'None of this is necessary for the example to work, but I know I'll need to use an inherited base class later on so I threw it into the example now.
    
        Protected ReadOnly Property PluginName As String
            Get
                Return CustomAttributes("AssemblyPluginNameAttribute")
            End Get
        End Property
    
        Protected ReadOnly Property PluginGUID As String
            Get
                Return CustomAttributes("AssemblyPluginGUIDAttribute")
            End Get
        End Property
    
        Protected IsInitialized As Boolean = False
        Protected CustomAttributes As Dictionary(Of String, String)
    
        Protected Sub Initialize()
            CustomAttributes = New Dictionary(Of String, String)
            Dim attribs = Me.GetType.Assembly.GetCustomAttributesData
            For Each attrib In attribs
                Dim name As String = attrib.Constructor.DeclaringType.Name
                Dim value As String
                If attrib.ConstructorArguments.Count = 0 Then
                    value = ""
                Else
                    value = attrib.ConstructorArguments(0).ToString.Replace("""", "")
                End If
                CustomAttributes.Add(name, value)
            Next
            IsInitialized = True
        End Sub
    End Class
    
    <AttributeUsage(AttributeTargets.Assembly)>
    Public Class AssemblyPluginNameAttribute
        Inherits System.Attribute
    
        Private _name As String
    
        Public Sub New(ByVal value As String)
            _name = value
        End Sub
    
        Public Overridable ReadOnly Property PluginName As String
            Get
                Return _name
            End Get
        End Property
    End Class
    
    <AttributeUsage(AttributeTargets.Assembly)>
    Public Class AssemblyPluginGUIDAttribute
        Inherits System.Attribute
    
        Private _g As String
    
        Public Sub New(ByVal value As String)
            _g = value
        End Sub
    
        Public Overridable ReadOnly Property PluginGUID As String
            Get
                Return _g
            End Get
        End Property
    End Class
    
    一旦你完成了你需要做的程序

    additionPlugin.PluginInterface.Calculate(3, 2)
    
    …并检索结果

    Console.WriteLine("3 + 2 = {0}", additionPlugin.PluginInterface.Result)
    
    …只需卸载插件:

    additionPlugin.Unload()
    
    如果您需要在包装器仍在内存中时重新加载它,只需再次调用
    Load()
    方法-创建新AppDomain和重新加载程序集所需的所有信息都在那里。并且,在回答我的初始问题时,
    Unload()方法
    方法已被调用,程序集将被释放,并且可以根据需要进行替换/升级,这是执行此操作的首要目的

    我之前被绊倒的部分原因是我没有将SharedObjects.dll文件包含在与插件相同的文件夹中。我发现任何引用的程序集都需要存在。因此,在我的插件和共享对象项目的构建后事件中,我有以下内容:
    xcopy/y$(ProjectDir)$(OutDir)$(TargetFileName)c:\path\to\Plugins
    。每次构建解决方案时,所有DLL都会放在需要的文件夹中


    抱歉,如果这有点长,但这有点复杂。可能有一个较短的方法来完成…但目前,这可以满足我的所有需要。

    您正在appdomain中创建一个Loader实例。因此,现在Loader类型同时加载在主appdomain和插件appdomain中。您需要一个接口,而不是decla红色是一个单独的程序集,可以在两个程序中加载。插件需要实现它。
    additionPlugin.PluginInterface.Calculate(3, 2)
    
    Console.WriteLine("3 + 2 = {0}", additionPlugin.PluginInterface.Result)
    
    additionPlugin.Unload()