Vb.net 卸载DLL直到它';需要什么
我在这里读到的关于使用AppDomains卸载插件DLL的一些答案让我很难受。以下是我的架构: 在我的解决方案中,我有一个Vb.net 卸载DLL直到它';需要什么,vb.net,dll,Vb.net,Dll,我在这里读到的关于使用AppDomains卸载插件DLL的一些答案让我很难受。以下是我的架构: 在我的解决方案中,我有一个SharedObjects项目,其中包含一个ModuleBase类,所有插件(解决方案中的独立项目)都继承该类。在SharedObjects项目中,我还有一个所有插件都可以实现的接口(因此,如果我有六个插件,它们都实现了相同的接口,因此使用这些插件的主程序在编译插件时不需要知道甚至不关心插件类的名称;它们都实现了相同的接口,因此公开了相同的信息)。每个插件项目都有一个对Sha
SharedObjects
项目,其中包含一个ModuleBase
类,所有插件(解决方案中的独立项目)都继承该类。在SharedObjects
项目中,我还有一个所有插件都可以实现的接口(因此,如果我有六个插件,它们都实现了相同的接口,因此使用这些插件的主程序在编译插件时不需要知道甚至不关心插件类的名称;它们都实现了相同的接口,因此公开了相同的信息)。每个插件项目都有一个对SharedObjects
项目的项目引用。(作为旁注,可能很重要,也可能不重要,SharedObjects
项目有一个对另一个解决方案的项目引用,CompanyObjects
包含许多常用的类、类型、对象等。)当所有这些都完成后,当任何给定插件编译时,输出目录包含以下DLL:
SharedObjects
项目中的DLL公司对象
项目中的DLL公司对象
项目中引用的四个必备第三方DLLpluginhelp
,存储在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()