Vb.net 如果不关闭整个应用程序,则无法关闭/卸载插件

Vb.net 如果不关闭整个应用程序,则无法关闭/卸载插件,vb.net,Vb.net,我需要一些关于我的应用程序的IPlugin实现的帮助 插件合同代码: Public Interface IPlugin ReadOnly Property Name() As String Sub DoSomething() Sub DoExit() End Interface 主应用程序: Imports PluginContracts Module Program Sub Main() LoadTray() End Sub S

我需要一些关于我的应用程序的IPlugin实现的帮助

插件合同代码:

Public Interface IPlugin
    ReadOnly Property Name() As String
    Sub DoSomething()
    Sub DoExit()
End Interface
主应用程序:

Imports PluginContracts
Module Program
    Sub Main()
        LoadTray()
    End Sub
    Sub LoadTray()
        Dim dll As Assembly = Assembly.LoadFrom(GetDLL.TrayDll)
        For Each t As Type In dll.GetTypes
            If Not t.GetInterface("IPlugin") = Nothing Then
                Try
                    Dim PluginClass As IPlugin =  Type(Activator.CreateInstance(t), IPlugin)
                    PluginClass.DoSomething()
                Catch ex As Exception
                    MessageBox.Show(ex.ToString())
                End Try
            End If
        Next
    End Sub
End Module
插件:

Imports PluginContracts
Imports System.Windows.Forms

Public Class Plugin
    Implements IPlugin
    Public Sub DoSomething() Implements IPlugin.DoSomething
        Application.Run(New MyContext)
    End Sub
    Public Sub New()
    End Sub
    Public Sub DoExit() Implements IPlugin.DoExit
        Application.Exit()
    End Sub
    Public ReadOnly Property Name As String Implements IPlugin.Name
        Get
            Name = "First Plugin"
        End Get
    End Property
End Class
(插件应用程序是一个Dll,类“MyContext”中有一个托盘图标)

我有一切工作,插件加载(与托盘图标),但我不能关闭它和加载其他东西。 我有一个FileSystemWatcher,它将关闭插件,更新Dll,然后重新打开它,但它会关闭主应用程序,我不能做任何其他事情


感谢您的帮助

很抱歉花了这么长时间回答。这比我想象的要复杂得多,至少如果你想对你的插件施加安全限制的话

不安全的方式 如果您同意插件以所谓的完全信任方式执行,也就是说它可以做任何它想做的事情,那么您必须做三件事:

  • 调整代码以在单独的
    AppDomain
    中启动插件

  • 在自己的线程中运行
    DoSomething()
    方法(这是必要的,因为
    Application.Run()
    尝试创建新的UI线程)

  • IPlugin
    接口更改为
    MustInherit
    类。这是因为在不同的
    AppDomains
    之间封送的代码必须在继承自的对象中运行

  • 新代码:

    编辑(2018-05-29)

    我认为在无窗口应用程序中等待插件退出的最佳方法是使用一个插件

    我已经在下面的代码中对此进行了快速实现关闭插件时,现在必须调用
    PluginClass.Exit()
    ,而不是
    DoExit()

    在进行更多测试后,我会将其添加到更安全的解决方案中

    PluginBase.vb:

    Public MustInherit Class PluginBase
        Inherits MarshalByRefObject
    
        Private IsExitingEvent As New ManualResetEvent(False)
    
        Public MustOverride ReadOnly Property Name As String
        Public MustOverride Sub DoSomething()
        Protected MustOverride Sub OnExit()
    
        Public Sub [Exit]()
            Me.OnExit()
            Me.IsExitingEvent.Set()
        End Sub
    
       Public Sub WaitForExit()
           Me.IsExitingEvent.WaitOne(-1)
       End Sub
    End Class
    
    Imports PluginContracts
    Imports System.Windows.Forms
    
    Public Class Plugin
        Inherits PluginBase
    
        Protected Overrides Sub DoSomething()
            Application.Run(New MyContext)
        End Sub
    
        Protected Overrides Sub OnExit()
            Application.Exit()
        End Sub
    
        Public Overrides ReadOnly Property Name As String
            Get
                Return "First Plugin" 'Use "Return" instead of "Name = ..."
            End Get
        End Property
    End Class
    
    ''' <summary>
    ''' A base class for application plugins.
    ''' </summary>
    ''' <remarks></remarks>
    Public MustInherit Class PluginBase
        Inherits MarshalByRefObject
    
        Public MustOverride ReadOnly Property Name As String
        Public MustOverride Sub DoSomething()
        Public MustOverride Sub OnExit()
    End Class
    
    Imports System.Security
    Imports System.Runtime.Remoting
    
    ''' <summary>
    ''' A class holding information about a plugin.
    ''' </summary>
    ''' <remarks></remarks>
    <SecurityCritical()>
    Public Class PluginInfo
    
        Private _file As String
        Private _plugin As PluginBase
        Private _appDomain As AppDomain
    
        Private Unloader As PluginUnloader
        Friend Unloaded As Boolean = False
    
        ''' <summary>
        ''' Gets the AppDomain that this plugin runs in.
        ''' </summary>
        ''' <remarks></remarks>
        Friend ReadOnly Property AppDomain As AppDomain
            Get
                Return _appDomain
            End Get
        End Property
    
        ''' <summary>
        ''' Gets the full path to the plugin assembly.
        ''' </summary>
        ''' <remarks></remarks>
        Public ReadOnly Property File As String
            Get
                Return _file
            End Get
        End Property
    
        ''' <summary>
        ''' Gets the underlying plugin.
        ''' </summary>
        ''' <remarks></remarks>
        Public ReadOnly Property Plugin As PluginBase
            Get
                Return _plugin
            End Get
        End Property
    
        ''' <summary>
        ''' DO NOT USE! See PluginManager.UnloadPlugin() instead.
        ''' </summary>
        ''' <remarks></remarks>
        <SecurityCritical()>
        Friend Sub Unload()
            Me.Unloader.Unload()
            Me.Unloaded = True
        End Sub
    
        ''' <summary>
        ''' Initializes a new instance of the PluginInfo class.
        ''' </summary>
        ''' <param name="File">The full path to the plugin assembly.</param>
        ''' <param name="Plugin">The underlying plugin.</param>
        ''' <param name="AppDomain">The AppDomain that the plugin runs in.</param>
        ''' <remarks></remarks>
        <SecurityCritical()>
        Friend Sub New(ByVal File As String, ByVal Plugin As PluginBase, ByVal AppDomain As AppDomain)
            _file = File
            _plugin = Plugin
            _appDomain = AppDomain
    
            'Create an instance of PluginUnloader inside the plugin's AppDomain.
            Dim Handle As ObjectHandle = Activator.CreateInstanceFrom(Me.AppDomain, GetType(PluginUnloader).Module.FullyQualifiedName, GetType(PluginUnloader).FullName)
            Me.Unloader = CType(Handle.Unwrap(), PluginUnloader)
        End Sub
    End Class
    
    '-------------------------------------------------------------------------------
    'Copyright (c) 2018, Vincent Bengtsson
    'All rights reserved.
    '
    'Redistribution and use in source and binary forms, with or without
    'modification, are permitted provided that the following conditions are met:
    '1. Redistributions of source code must retain the above copyright notice, this
    '   list of conditions and the following disclaimer.
    '2. Redistributions in binary form must reproduce the above copyright notice,
    '   this list of conditions and the following disclaimer in the documentation
    '   and/or other materials provided with the distribution.
    '
    'THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
    'ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    'WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    'DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
    'ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    '(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
    'LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
    'ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    '(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
    'SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    '-------------------------------------------------------------------------------
    
    Imports System.Collections.ObjectModel
    Imports System.Reflection
    Imports System.IO
    Imports System.Security
    Imports System.Security.Permissions
    Imports System.Security.Policy
    
    ''' <summary>
    ''' A class for managing application plugins.
    ''' </summary>
    ''' <remarks></remarks>
    <SecurityCritical()>
    Public NotInheritable Class PluginManager
        Implements IDisposable
    
        Private PluginLookup As New Dictionary(Of String, PluginInfo)
        Private PluginList As New List(Of String)
        Private CurrentAppDomain As AppDomain = Nothing
    
        Private _loadedPlugins As New ReadOnlyCollection(Of String)(Me.PluginList)
    
        ''' <summary>
        ''' Gets a list of all loaded plugins' names.
        ''' </summary>
        ''' <remarks></remarks>
        Public ReadOnly Property LoadedPlugins As ReadOnlyCollection(Of String)
            Get
                Return _loadedPlugins
            End Get
        End Property
    
        ''' <summary>
        ''' Returns the plugin with the specified name (or null, if the plugin isn't loaded).
        ''' </summary>
        ''' <param name="Name">The name of the plugin to get.</param>
        ''' <remarks></remarks>
        <SecurityCritical()>
        Public Function GetPluginByName(ByVal Name As String) As PluginInfo
            Dim Plugin As PluginInfo = Nothing
            Me.PluginLookup.TryGetValue(Name, Plugin)
            Return Plugin
        End Function
    
        ''' <summary>
        ''' Checks whether a plugin by the specified name is loaded.
        ''' </summary>
        ''' <param name="Name">The name of the plugin to look for.</param>
        ''' <remarks></remarks>
        <SecurityCritical()>
        Public Function IsPluginLoaded(ByVal Name As String) As Boolean
            Return Me.PluginLookup.ContainsKey(Name)
        End Function
    
        ''' <summary>
        ''' Loads a plugin with the specified permissions (or no permissions, if omitted).
        ''' </summary>
        ''' <param name="File">The path to the plugin assembly to load.</param>
        ''' <param name="Permissions">Optional. A list of permissions to give the plugin (default permissions that are always applied: SecurityPermissionFlag.Execution).</param>
        ''' <remarks></remarks>
        <SecurityCritical()>
        Public Function LoadPlugin(ByVal File As String, ByVal ParamArray Permissions As IPermission()) As PluginInfo
            Dim FullPath As String = Path.GetFullPath(File)
            If System.IO.File.Exists(FullPath) = False Then Throw New FileNotFoundException()
    
            'Check if the plugin file has already been loaded. This is to avoid odd errors caused by Assembly.LoadFrom().
            If Me.PluginLookup.Values.Any(Function(info As PluginInfo) info.File.Equals(FullPath, StringComparison.OrdinalIgnoreCase)) = True Then
                Throw New ApplicationException("Plugin """ & FullPath & """ is already loaded!")
            End If
    
            'Load assembly and look for a type derived from PluginBase.
            Dim PluginAssembly As Assembly = Assembly.LoadFrom(FullPath)
            Dim PluginType As Type = PluginManager.GetPluginType(PluginAssembly)
            If PluginType Is Nothing Then Throw New TypeLoadException("""" & FullPath & """ is not a valid plugin!")
    
            'Set up the application domain.
            'Setting PartialTrustVisibleAssemblies allows our plugin to make partially trusted calls to the PluginBase DLL.
            Dim PluginDomainSetup As New AppDomainSetup() With {
                .ApplicationBase = Me.CurrentAppDomain.BaseDirectory,
                .PartialTrustVisibleAssemblies = New String() {GetType(PluginUnloader).Assembly.GetName().Name & ", PublicKey=" & BitConverter.ToString(GetType(PluginUnloader).Assembly.GetName().GetPublicKey()).ToLower().Replace("-", "")}
            }
    
            'Set up the default (necessary) permissions for the plugin:
            '   SecurityPermissionFlag.Execution     - Allows our plugin to execute managed code.
            '   FileIOPermissionAccess.Read          - Allows our plugin to read its own assembly.
            '   FileIOPermissionAccess.PathDiscovery - Allows our plugin to get information about its parent directory.
            Dim PluginPermissions As New PermissionSet(PermissionState.None) 'No permissions to begin with.
            PluginPermissions.AddPermission(New SecurityPermission(SecurityPermissionFlag.Execution))
            PluginPermissions.AddPermission(New FileIOPermission(FileIOPermissionAccess.Read Or FileIOPermissionAccess.PathDiscovery, FullPath))
    
            'Load all additional permissions (if any).
            For Each Permission As IPermission In Permissions
                PluginPermissions.AddPermission(Permission)
            Next
    
            'Get the strong name for the assembly containing PluginUnloader and create the AppDomain.
            'The strong name is used so that PluginUnloader may bypass the above added restrictions.
            Dim TrustedAssembly As StrongName = GetType(PluginUnloader).Assembly.Evidence.GetHostEvidence(Of StrongName)()
            Dim PluginDomain As AppDomain = AppDomain.CreateDomain(File, Nothing, PluginDomainSetup, PluginPermissions, TrustedAssembly)
    
            'Create an instance of the plugin.
            Dim Plugin As PluginBase = CType(PluginDomain.CreateInstanceFromAndUnwrap(FullPath, PluginType.FullName), PluginBase)
            Dim PluginInfo As New PluginInfo(FullPath, Plugin, PluginDomain)
    
            'Is a plugin by this name already loaded?
            If Me.IsPluginLoaded(Plugin.Name) = True Then
                Dim Name As String = Plugin.Name
                Me.UnloadPlugin(PluginInfo)
    
                Throw New ApplicationException("A plugin by the name """ & Name & """ is already loaded!")
            End If
    
            'Add the plugin to our lookup table and name list.
            Me.PluginLookup.Add(Plugin.Name, PluginInfo)
            Me.PluginList.Add(Plugin.Name)
    
            'Return the loaded plugin to the caller.
            Return PluginInfo
        End Function
    
        ''' <summary>
        ''' Unloads a plugin.
        ''' </summary>
        ''' <param name="Name">The name of the plugin to unload.</param>
        ''' <remarks></remarks>
        <SecurityCritical()>
        Public Sub UnloadPlugin(ByVal Name As String)
            Dim Plugin As PluginInfo = Me.GetPluginByName(Name)
            If Plugin Is Nothing Then Throw New ArgumentException("No plugin by the name """ & Name & """ is loaded.", "Name")
            Me.UnloadPlugin(Plugin)
        End Sub
    
        ''' <summary>
        ''' Unloads a plugin.
        ''' </summary>
        ''' <param name="PluginInfo">The plugin to unload.</param>
        ''' <remarks></remarks>
        <SecurityCritical()>
        Public Sub UnloadPlugin(ByVal PluginInfo As PluginInfo)
            If PluginInfo Is Nothing Then Throw New ArgumentNullException("PluginInfo")
            If PluginInfo.Unloaded = True Then Return
            Dim PluginName As String = PluginInfo.Plugin.Name
    
            Dim Permission As New SecurityPermission(SecurityPermissionFlag.ControlAppDomain)
            Permission.Assert()
    
            PluginInfo.Plugin.OnExit()
            PluginInfo.Unload()
            AppDomain.Unload(PluginInfo.AppDomain)
    
            CodeAccessPermission.RevertAssert()
    
            Me.PluginLookup.Remove(PluginName)
        End Sub
    
        ''' <summary>
        ''' Attempts to get a class derived from PluginBase in the specified assembly.
        ''' </summary>
        ''' <param name="PluginAssembly">The assembly to check.</param>
        ''' <remarks></remarks>
        <SecurityCritical()>
        Private Shared Function GetPluginType(ByVal PluginAssembly As Assembly) As Type
            For Each t As Type In PluginAssembly.GetTypes()
                If GetType(PluginBase).IsAssignableFrom(t) = True Then Return t
            Next
            Return Nothing
        End Function
    
        ''' <summary>
        ''' Initializes a new instance of the PluginManager class.
        ''' </summary>
        ''' <remarks></remarks>
        <SecurityCritical()>
        Public Sub New()
            Me.CurrentAppDomain = AppDomain.CurrentDomain
        End Sub
    
    #Region "IDisposable Support"
        Private disposedValue As Boolean ' To detect redundant calls
    
        ' IDisposable
        <SecurityCritical()>
        Protected Sub Dispose(disposing As Boolean)
            If Not Me.disposedValue Then
                If disposing Then
                    ' TODO: dispose managed state (managed objects).
    
                    'Unload all plugins.
                    For Each PluginPair As KeyValuePair(Of String, PluginInfo) In Me.PluginLookup
                        Try : Me.UnloadPlugin(PluginPair.Value) : Catch : End Try
                    Next
                End If
    
                ' TODO: free unmanaged resources (unmanaged objects) and override Finalize() below.
                ' TODO: set large fields to null.
            End If
            Me.disposedValue = True
        End Sub
    
        ' TODO: override Finalize() only if Dispose(ByVal disposing As Boolean) above has code to free unmanaged resources.
        'Protected Overrides Sub Finalize()
        '    ' Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
        '    Dispose(False)
        '    MyBase.Finalize()
        'End Sub
    
        ' This code added by Visual Basic to correctly implement the disposable pattern.
        <SecurityCritical()>
        Public Sub Dispose() Implements IDisposable.Dispose
            ' Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
            Dispose(True)
            GC.SuppressFinalize(Me)
        End Sub
    #End Region
    
    End Class
    
    Imports System.Windows.Forms
    Imports System.Security
    Imports System.Security.Permissions
    
    ''' <summary>
    ''' A class for unloading plugins from within their AppDomains.
    ''' </summary>
    ''' <remarks></remarks>
    <SecurityCritical()>
    Public NotInheritable Class PluginUnloader
        Inherits MarshalByRefObject
    
        ''' <summary>
        ''' Calls Application.Exit(). This must be called inside the plugin's AppDomain.
        ''' </summary>
        ''' <remarks></remarks>
        <SecurityCritical()>
        Public Sub Unload()
            'Request permission to execute managed code (required to call Application.Exit()).
            Dim Permission As New SecurityPermission(SecurityPermissionFlag.UnmanagedCode)
            Permission.Assert()
    
            'Exits the plugin's UI threads (if any exist).
            Application.Exit()
    
            'Revert UnmanagedCode privilege.
            CodeAccessPermission.RevertAssert()
        End Sub
    
        ''' <summary>
        ''' Initializes a new instance of the PluginUnloader class.
        ''' </summary>
        ''' <remarks></remarks>
        <SecurityCritical()>
        Public Sub New()
        End Sub
    End Class
    
    您的插件:

    Public MustInherit Class PluginBase
        Inherits MarshalByRefObject
    
        Private IsExitingEvent As New ManualResetEvent(False)
    
        Public MustOverride ReadOnly Property Name As String
        Public MustOverride Sub DoSomething()
        Protected MustOverride Sub OnExit()
    
        Public Sub [Exit]()
            Me.OnExit()
            Me.IsExitingEvent.Set()
        End Sub
    
       Public Sub WaitForExit()
           Me.IsExitingEvent.WaitOne(-1)
       End Sub
    End Class
    
    Imports PluginContracts
    Imports System.Windows.Forms
    
    Public Class Plugin
        Inherits PluginBase
    
        Protected Overrides Sub DoSomething()
            Application.Run(New MyContext)
        End Sub
    
        Protected Overrides Sub OnExit()
            Application.Exit()
        End Sub
    
        Public Overrides ReadOnly Property Name As String
            Get
                Return "First Plugin" 'Use "Return" instead of "Name = ..."
            End Get
        End Property
    End Class
    
    ''' <summary>
    ''' A base class for application plugins.
    ''' </summary>
    ''' <remarks></remarks>
    Public MustInherit Class PluginBase
        Inherits MarshalByRefObject
    
        Public MustOverride ReadOnly Property Name As String
        Public MustOverride Sub DoSomething()
        Public MustOverride Sub OnExit()
    End Class
    
    Imports System.Security
    Imports System.Runtime.Remoting
    
    ''' <summary>
    ''' A class holding information about a plugin.
    ''' </summary>
    ''' <remarks></remarks>
    <SecurityCritical()>
    Public Class PluginInfo
    
        Private _file As String
        Private _plugin As PluginBase
        Private _appDomain As AppDomain
    
        Private Unloader As PluginUnloader
        Friend Unloaded As Boolean = False
    
        ''' <summary>
        ''' Gets the AppDomain that this plugin runs in.
        ''' </summary>
        ''' <remarks></remarks>
        Friend ReadOnly Property AppDomain As AppDomain
            Get
                Return _appDomain
            End Get
        End Property
    
        ''' <summary>
        ''' Gets the full path to the plugin assembly.
        ''' </summary>
        ''' <remarks></remarks>
        Public ReadOnly Property File As String
            Get
                Return _file
            End Get
        End Property
    
        ''' <summary>
        ''' Gets the underlying plugin.
        ''' </summary>
        ''' <remarks></remarks>
        Public ReadOnly Property Plugin As PluginBase
            Get
                Return _plugin
            End Get
        End Property
    
        ''' <summary>
        ''' DO NOT USE! See PluginManager.UnloadPlugin() instead.
        ''' </summary>
        ''' <remarks></remarks>
        <SecurityCritical()>
        Friend Sub Unload()
            Me.Unloader.Unload()
            Me.Unloaded = True
        End Sub
    
        ''' <summary>
        ''' Initializes a new instance of the PluginInfo class.
        ''' </summary>
        ''' <param name="File">The full path to the plugin assembly.</param>
        ''' <param name="Plugin">The underlying plugin.</param>
        ''' <param name="AppDomain">The AppDomain that the plugin runs in.</param>
        ''' <remarks></remarks>
        <SecurityCritical()>
        Friend Sub New(ByVal File As String, ByVal Plugin As PluginBase, ByVal AppDomain As AppDomain)
            _file = File
            _plugin = Plugin
            _appDomain = AppDomain
    
            'Create an instance of PluginUnloader inside the plugin's AppDomain.
            Dim Handle As ObjectHandle = Activator.CreateInstanceFrom(Me.AppDomain, GetType(PluginUnloader).Module.FullyQualifiedName, GetType(PluginUnloader).FullName)
            Me.Unloader = CType(Handle.Unwrap(), PluginUnloader)
        End Sub
    End Class
    
    '-------------------------------------------------------------------------------
    'Copyright (c) 2018, Vincent Bengtsson
    'All rights reserved.
    '
    'Redistribution and use in source and binary forms, with or without
    'modification, are permitted provided that the following conditions are met:
    '1. Redistributions of source code must retain the above copyright notice, this
    '   list of conditions and the following disclaimer.
    '2. Redistributions in binary form must reproduce the above copyright notice,
    '   this list of conditions and the following disclaimer in the documentation
    '   and/or other materials provided with the distribution.
    '
    'THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
    'ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    'WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    'DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
    'ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    '(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
    'LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
    'ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    '(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
    'SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    '-------------------------------------------------------------------------------
    
    Imports System.Collections.ObjectModel
    Imports System.Reflection
    Imports System.IO
    Imports System.Security
    Imports System.Security.Permissions
    Imports System.Security.Policy
    
    ''' <summary>
    ''' A class for managing application plugins.
    ''' </summary>
    ''' <remarks></remarks>
    <SecurityCritical()>
    Public NotInheritable Class PluginManager
        Implements IDisposable
    
        Private PluginLookup As New Dictionary(Of String, PluginInfo)
        Private PluginList As New List(Of String)
        Private CurrentAppDomain As AppDomain = Nothing
    
        Private _loadedPlugins As New ReadOnlyCollection(Of String)(Me.PluginList)
    
        ''' <summary>
        ''' Gets a list of all loaded plugins' names.
        ''' </summary>
        ''' <remarks></remarks>
        Public ReadOnly Property LoadedPlugins As ReadOnlyCollection(Of String)
            Get
                Return _loadedPlugins
            End Get
        End Property
    
        ''' <summary>
        ''' Returns the plugin with the specified name (or null, if the plugin isn't loaded).
        ''' </summary>
        ''' <param name="Name">The name of the plugin to get.</param>
        ''' <remarks></remarks>
        <SecurityCritical()>
        Public Function GetPluginByName(ByVal Name As String) As PluginInfo
            Dim Plugin As PluginInfo = Nothing
            Me.PluginLookup.TryGetValue(Name, Plugin)
            Return Plugin
        End Function
    
        ''' <summary>
        ''' Checks whether a plugin by the specified name is loaded.
        ''' </summary>
        ''' <param name="Name">The name of the plugin to look for.</param>
        ''' <remarks></remarks>
        <SecurityCritical()>
        Public Function IsPluginLoaded(ByVal Name As String) As Boolean
            Return Me.PluginLookup.ContainsKey(Name)
        End Function
    
        ''' <summary>
        ''' Loads a plugin with the specified permissions (or no permissions, if omitted).
        ''' </summary>
        ''' <param name="File">The path to the plugin assembly to load.</param>
        ''' <param name="Permissions">Optional. A list of permissions to give the plugin (default permissions that are always applied: SecurityPermissionFlag.Execution).</param>
        ''' <remarks></remarks>
        <SecurityCritical()>
        Public Function LoadPlugin(ByVal File As String, ByVal ParamArray Permissions As IPermission()) As PluginInfo
            Dim FullPath As String = Path.GetFullPath(File)
            If System.IO.File.Exists(FullPath) = False Then Throw New FileNotFoundException()
    
            'Check if the plugin file has already been loaded. This is to avoid odd errors caused by Assembly.LoadFrom().
            If Me.PluginLookup.Values.Any(Function(info As PluginInfo) info.File.Equals(FullPath, StringComparison.OrdinalIgnoreCase)) = True Then
                Throw New ApplicationException("Plugin """ & FullPath & """ is already loaded!")
            End If
    
            'Load assembly and look for a type derived from PluginBase.
            Dim PluginAssembly As Assembly = Assembly.LoadFrom(FullPath)
            Dim PluginType As Type = PluginManager.GetPluginType(PluginAssembly)
            If PluginType Is Nothing Then Throw New TypeLoadException("""" & FullPath & """ is not a valid plugin!")
    
            'Set up the application domain.
            'Setting PartialTrustVisibleAssemblies allows our plugin to make partially trusted calls to the PluginBase DLL.
            Dim PluginDomainSetup As New AppDomainSetup() With {
                .ApplicationBase = Me.CurrentAppDomain.BaseDirectory,
                .PartialTrustVisibleAssemblies = New String() {GetType(PluginUnloader).Assembly.GetName().Name & ", PublicKey=" & BitConverter.ToString(GetType(PluginUnloader).Assembly.GetName().GetPublicKey()).ToLower().Replace("-", "")}
            }
    
            'Set up the default (necessary) permissions for the plugin:
            '   SecurityPermissionFlag.Execution     - Allows our plugin to execute managed code.
            '   FileIOPermissionAccess.Read          - Allows our plugin to read its own assembly.
            '   FileIOPermissionAccess.PathDiscovery - Allows our plugin to get information about its parent directory.
            Dim PluginPermissions As New PermissionSet(PermissionState.None) 'No permissions to begin with.
            PluginPermissions.AddPermission(New SecurityPermission(SecurityPermissionFlag.Execution))
            PluginPermissions.AddPermission(New FileIOPermission(FileIOPermissionAccess.Read Or FileIOPermissionAccess.PathDiscovery, FullPath))
    
            'Load all additional permissions (if any).
            For Each Permission As IPermission In Permissions
                PluginPermissions.AddPermission(Permission)
            Next
    
            'Get the strong name for the assembly containing PluginUnloader and create the AppDomain.
            'The strong name is used so that PluginUnloader may bypass the above added restrictions.
            Dim TrustedAssembly As StrongName = GetType(PluginUnloader).Assembly.Evidence.GetHostEvidence(Of StrongName)()
            Dim PluginDomain As AppDomain = AppDomain.CreateDomain(File, Nothing, PluginDomainSetup, PluginPermissions, TrustedAssembly)
    
            'Create an instance of the plugin.
            Dim Plugin As PluginBase = CType(PluginDomain.CreateInstanceFromAndUnwrap(FullPath, PluginType.FullName), PluginBase)
            Dim PluginInfo As New PluginInfo(FullPath, Plugin, PluginDomain)
    
            'Is a plugin by this name already loaded?
            If Me.IsPluginLoaded(Plugin.Name) = True Then
                Dim Name As String = Plugin.Name
                Me.UnloadPlugin(PluginInfo)
    
                Throw New ApplicationException("A plugin by the name """ & Name & """ is already loaded!")
            End If
    
            'Add the plugin to our lookup table and name list.
            Me.PluginLookup.Add(Plugin.Name, PluginInfo)
            Me.PluginList.Add(Plugin.Name)
    
            'Return the loaded plugin to the caller.
            Return PluginInfo
        End Function
    
        ''' <summary>
        ''' Unloads a plugin.
        ''' </summary>
        ''' <param name="Name">The name of the plugin to unload.</param>
        ''' <remarks></remarks>
        <SecurityCritical()>
        Public Sub UnloadPlugin(ByVal Name As String)
            Dim Plugin As PluginInfo = Me.GetPluginByName(Name)
            If Plugin Is Nothing Then Throw New ArgumentException("No plugin by the name """ & Name & """ is loaded.", "Name")
            Me.UnloadPlugin(Plugin)
        End Sub
    
        ''' <summary>
        ''' Unloads a plugin.
        ''' </summary>
        ''' <param name="PluginInfo">The plugin to unload.</param>
        ''' <remarks></remarks>
        <SecurityCritical()>
        Public Sub UnloadPlugin(ByVal PluginInfo As PluginInfo)
            If PluginInfo Is Nothing Then Throw New ArgumentNullException("PluginInfo")
            If PluginInfo.Unloaded = True Then Return
            Dim PluginName As String = PluginInfo.Plugin.Name
    
            Dim Permission As New SecurityPermission(SecurityPermissionFlag.ControlAppDomain)
            Permission.Assert()
    
            PluginInfo.Plugin.OnExit()
            PluginInfo.Unload()
            AppDomain.Unload(PluginInfo.AppDomain)
    
            CodeAccessPermission.RevertAssert()
    
            Me.PluginLookup.Remove(PluginName)
        End Sub
    
        ''' <summary>
        ''' Attempts to get a class derived from PluginBase in the specified assembly.
        ''' </summary>
        ''' <param name="PluginAssembly">The assembly to check.</param>
        ''' <remarks></remarks>
        <SecurityCritical()>
        Private Shared Function GetPluginType(ByVal PluginAssembly As Assembly) As Type
            For Each t As Type In PluginAssembly.GetTypes()
                If GetType(PluginBase).IsAssignableFrom(t) = True Then Return t
            Next
            Return Nothing
        End Function
    
        ''' <summary>
        ''' Initializes a new instance of the PluginManager class.
        ''' </summary>
        ''' <remarks></remarks>
        <SecurityCritical()>
        Public Sub New()
            Me.CurrentAppDomain = AppDomain.CurrentDomain
        End Sub
    
    #Region "IDisposable Support"
        Private disposedValue As Boolean ' To detect redundant calls
    
        ' IDisposable
        <SecurityCritical()>
        Protected Sub Dispose(disposing As Boolean)
            If Not Me.disposedValue Then
                If disposing Then
                    ' TODO: dispose managed state (managed objects).
    
                    'Unload all plugins.
                    For Each PluginPair As KeyValuePair(Of String, PluginInfo) In Me.PluginLookup
                        Try : Me.UnloadPlugin(PluginPair.Value) : Catch : End Try
                    Next
                End If
    
                ' TODO: free unmanaged resources (unmanaged objects) and override Finalize() below.
                ' TODO: set large fields to null.
            End If
            Me.disposedValue = True
        End Sub
    
        ' TODO: override Finalize() only if Dispose(ByVal disposing As Boolean) above has code to free unmanaged resources.
        'Protected Overrides Sub Finalize()
        '    ' Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
        '    Dispose(False)
        '    MyBase.Finalize()
        'End Sub
    
        ' This code added by Visual Basic to correctly implement the disposable pattern.
        <SecurityCritical()>
        Public Sub Dispose() Implements IDisposable.Dispose
            ' Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
            Dispose(True)
            GC.SuppressFinalize(Me)
        End Sub
    #End Region
    
    End Class
    
    Imports System.Windows.Forms
    Imports System.Security
    Imports System.Security.Permissions
    
    ''' <summary>
    ''' A class for unloading plugins from within their AppDomains.
    ''' </summary>
    ''' <remarks></remarks>
    <SecurityCritical()>
    Public NotInheritable Class PluginUnloader
        Inherits MarshalByRefObject
    
        ''' <summary>
        ''' Calls Application.Exit(). This must be called inside the plugin's AppDomain.
        ''' </summary>
        ''' <remarks></remarks>
        <SecurityCritical()>
        Public Sub Unload()
            'Request permission to execute managed code (required to call Application.Exit()).
            Dim Permission As New SecurityPermission(SecurityPermissionFlag.UnmanagedCode)
            Permission.Assert()
    
            'Exits the plugin's UI threads (if any exist).
            Application.Exit()
    
            'Revert UnmanagedCode privilege.
            CodeAccessPermission.RevertAssert()
        End Sub
    
        ''' <summary>
        ''' Initializes a new instance of the PluginUnloader class.
        ''' </summary>
        ''' <remarks></remarks>
        <SecurityCritical()>
        Public Sub New()
        End Sub
    End Class
    
    但是,因为让插件完全信任运行是非常不安全的
    我设计了一个解决方案,可以让你控制插件的功能。这需要我重写大部分代码,因为它需要不同的结构才能工作


    安全之路 在我的测试用例中,
    PluginContracts
    是一个单独的DLL(项目),只有四个类:

    • PluginBase
      -所有插件的基类
    • PluginInfo
      -包含插件信息的包装(由
      PluginManager
      使用)
    • PluginManager
      -用于加载、卸载和跟踪插件的管理器
    • PluginUnloader
      -在插件的受限
      AppDomain
      中完全信任运行的类。仅用于调用
      Application.Exit()
    首先也是最重要的一点是,要使一切正常运行,
    PluginContracts
    DLL需要使用强名称签名。以下是如何做到这一点:

  • 右键单击
    解决方案资源管理器中的
    PluginContracts
    项目,然后按
    Properties

  • 选择签名选项卡

  • 选中显示为程序集签名的复选框,只保留延迟签名未选中

  • 打开下拉列表并按

  • 给密钥一个文件名和密码(一定要记住这一点!)

  • 完成了

  • 现在已经修复了这个问题,您需要使部分受信任的代码可以调用
    PluginContracts
    DLL。这是为了我们的插件可以使用它,因为它将作为不受信任的代码运行

  • 解决方案资源管理器中再次选择项目

  • 按下
    解决方案资源管理器中显示
    显示所有文件的按钮

  • 展开
    myproject
    节点

  • 双击
    AssemblyInfo.vb
    对其进行编辑,并在文件末尾添加此行:

    <Assembly: AllowPartiallyTrustedCallers(PartialTrustVisibilityLevel:=Security.PartialTrustVisibilityLevel.NotVisibleByDefault)> 
    
    PluginInfo.vb:

    Public MustInherit Class PluginBase
        Inherits MarshalByRefObject
    
        Private IsExitingEvent As New ManualResetEvent(False)
    
        Public MustOverride ReadOnly Property Name As String
        Public MustOverride Sub DoSomething()
        Protected MustOverride Sub OnExit()
    
        Public Sub [Exit]()
            Me.OnExit()
            Me.IsExitingEvent.Set()
        End Sub
    
       Public Sub WaitForExit()
           Me.IsExitingEvent.WaitOne(-1)
       End Sub
    End Class
    
    Imports PluginContracts
    Imports System.Windows.Forms
    
    Public Class Plugin
        Inherits PluginBase
    
        Protected Overrides Sub DoSomething()
            Application.Run(New MyContext)
        End Sub
    
        Protected Overrides Sub OnExit()
            Application.Exit()
        End Sub
    
        Public Overrides ReadOnly Property Name As String
            Get
                Return "First Plugin" 'Use "Return" instead of "Name = ..."
            End Get
        End Property
    End Class
    
    ''' <summary>
    ''' A base class for application plugins.
    ''' </summary>
    ''' <remarks></remarks>
    Public MustInherit Class PluginBase
        Inherits MarshalByRefObject
    
        Public MustOverride ReadOnly Property Name As String
        Public MustOverride Sub DoSomething()
        Public MustOverride Sub OnExit()
    End Class
    
    Imports System.Security
    Imports System.Runtime.Remoting
    
    ''' <summary>
    ''' A class holding information about a plugin.
    ''' </summary>
    ''' <remarks></remarks>
    <SecurityCritical()>
    Public Class PluginInfo
    
        Private _file As String
        Private _plugin As PluginBase
        Private _appDomain As AppDomain
    
        Private Unloader As PluginUnloader
        Friend Unloaded As Boolean = False
    
        ''' <summary>
        ''' Gets the AppDomain that this plugin runs in.
        ''' </summary>
        ''' <remarks></remarks>
        Friend ReadOnly Property AppDomain As AppDomain
            Get
                Return _appDomain
            End Get
        End Property
    
        ''' <summary>
        ''' Gets the full path to the plugin assembly.
        ''' </summary>
        ''' <remarks></remarks>
        Public ReadOnly Property File As String
            Get
                Return _file
            End Get
        End Property
    
        ''' <summary>
        ''' Gets the underlying plugin.
        ''' </summary>
        ''' <remarks></remarks>
        Public ReadOnly Property Plugin As PluginBase
            Get
                Return _plugin
            End Get
        End Property
    
        ''' <summary>
        ''' DO NOT USE! See PluginManager.UnloadPlugin() instead.
        ''' </summary>
        ''' <remarks></remarks>
        <SecurityCritical()>
        Friend Sub Unload()
            Me.Unloader.Unload()
            Me.Unloaded = True
        End Sub
    
        ''' <summary>
        ''' Initializes a new instance of the PluginInfo class.
        ''' </summary>
        ''' <param name="File">The full path to the plugin assembly.</param>
        ''' <param name="Plugin">The underlying plugin.</param>
        ''' <param name="AppDomain">The AppDomain that the plugin runs in.</param>
        ''' <remarks></remarks>
        <SecurityCritical()>
        Friend Sub New(ByVal File As String, ByVal Plugin As PluginBase, ByVal AppDomain As AppDomain)
            _file = File
            _plugin = Plugin
            _appDomain = AppDomain
    
            'Create an instance of PluginUnloader inside the plugin's AppDomain.
            Dim Handle As ObjectHandle = Activator.CreateInstanceFrom(Me.AppDomain, GetType(PluginUnloader).Module.FullyQualifiedName, GetType(PluginUnloader).FullName)
            Me.Unloader = CType(Handle.Unwrap(), PluginUnloader)
        End Sub
    End Class
    
    '-------------------------------------------------------------------------------
    'Copyright (c) 2018, Vincent Bengtsson
    'All rights reserved.
    '
    'Redistribution and use in source and binary forms, with or without
    'modification, are permitted provided that the following conditions are met:
    '1. Redistributions of source code must retain the above copyright notice, this
    '   list of conditions and the following disclaimer.
    '2. Redistributions in binary form must reproduce the above copyright notice,
    '   this list of conditions and the following disclaimer in the documentation
    '   and/or other materials provided with the distribution.
    '
    'THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
    'ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    'WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    'DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
    'ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    '(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
    'LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
    'ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    '(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
    'SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    '-------------------------------------------------------------------------------
    
    Imports System.Collections.ObjectModel
    Imports System.Reflection
    Imports System.IO
    Imports System.Security
    Imports System.Security.Permissions
    Imports System.Security.Policy
    
    ''' <summary>
    ''' A class for managing application plugins.
    ''' </summary>
    ''' <remarks></remarks>
    <SecurityCritical()>
    Public NotInheritable Class PluginManager
        Implements IDisposable
    
        Private PluginLookup As New Dictionary(Of String, PluginInfo)
        Private PluginList As New List(Of String)
        Private CurrentAppDomain As AppDomain = Nothing
    
        Private _loadedPlugins As New ReadOnlyCollection(Of String)(Me.PluginList)
    
        ''' <summary>
        ''' Gets a list of all loaded plugins' names.
        ''' </summary>
        ''' <remarks></remarks>
        Public ReadOnly Property LoadedPlugins As ReadOnlyCollection(Of String)
            Get
                Return _loadedPlugins
            End Get
        End Property
    
        ''' <summary>
        ''' Returns the plugin with the specified name (or null, if the plugin isn't loaded).
        ''' </summary>
        ''' <param name="Name">The name of the plugin to get.</param>
        ''' <remarks></remarks>
        <SecurityCritical()>
        Public Function GetPluginByName(ByVal Name As String) As PluginInfo
            Dim Plugin As PluginInfo = Nothing
            Me.PluginLookup.TryGetValue(Name, Plugin)
            Return Plugin
        End Function
    
        ''' <summary>
        ''' Checks whether a plugin by the specified name is loaded.
        ''' </summary>
        ''' <param name="Name">The name of the plugin to look for.</param>
        ''' <remarks></remarks>
        <SecurityCritical()>
        Public Function IsPluginLoaded(ByVal Name As String) As Boolean
            Return Me.PluginLookup.ContainsKey(Name)
        End Function
    
        ''' <summary>
        ''' Loads a plugin with the specified permissions (or no permissions, if omitted).
        ''' </summary>
        ''' <param name="File">The path to the plugin assembly to load.</param>
        ''' <param name="Permissions">Optional. A list of permissions to give the plugin (default permissions that are always applied: SecurityPermissionFlag.Execution).</param>
        ''' <remarks></remarks>
        <SecurityCritical()>
        Public Function LoadPlugin(ByVal File As String, ByVal ParamArray Permissions As IPermission()) As PluginInfo
            Dim FullPath As String = Path.GetFullPath(File)
            If System.IO.File.Exists(FullPath) = False Then Throw New FileNotFoundException()
    
            'Check if the plugin file has already been loaded. This is to avoid odd errors caused by Assembly.LoadFrom().
            If Me.PluginLookup.Values.Any(Function(info As PluginInfo) info.File.Equals(FullPath, StringComparison.OrdinalIgnoreCase)) = True Then
                Throw New ApplicationException("Plugin """ & FullPath & """ is already loaded!")
            End If
    
            'Load assembly and look for a type derived from PluginBase.
            Dim PluginAssembly As Assembly = Assembly.LoadFrom(FullPath)
            Dim PluginType As Type = PluginManager.GetPluginType(PluginAssembly)
            If PluginType Is Nothing Then Throw New TypeLoadException("""" & FullPath & """ is not a valid plugin!")
    
            'Set up the application domain.
            'Setting PartialTrustVisibleAssemblies allows our plugin to make partially trusted calls to the PluginBase DLL.
            Dim PluginDomainSetup As New AppDomainSetup() With {
                .ApplicationBase = Me.CurrentAppDomain.BaseDirectory,
                .PartialTrustVisibleAssemblies = New String() {GetType(PluginUnloader).Assembly.GetName().Name & ", PublicKey=" & BitConverter.ToString(GetType(PluginUnloader).Assembly.GetName().GetPublicKey()).ToLower().Replace("-", "")}
            }
    
            'Set up the default (necessary) permissions for the plugin:
            '   SecurityPermissionFlag.Execution     - Allows our plugin to execute managed code.
            '   FileIOPermissionAccess.Read          - Allows our plugin to read its own assembly.
            '   FileIOPermissionAccess.PathDiscovery - Allows our plugin to get information about its parent directory.
            Dim PluginPermissions As New PermissionSet(PermissionState.None) 'No permissions to begin with.
            PluginPermissions.AddPermission(New SecurityPermission(SecurityPermissionFlag.Execution))
            PluginPermissions.AddPermission(New FileIOPermission(FileIOPermissionAccess.Read Or FileIOPermissionAccess.PathDiscovery, FullPath))
    
            'Load all additional permissions (if any).
            For Each Permission As IPermission In Permissions
                PluginPermissions.AddPermission(Permission)
            Next
    
            'Get the strong name for the assembly containing PluginUnloader and create the AppDomain.
            'The strong name is used so that PluginUnloader may bypass the above added restrictions.
            Dim TrustedAssembly As StrongName = GetType(PluginUnloader).Assembly.Evidence.GetHostEvidence(Of StrongName)()
            Dim PluginDomain As AppDomain = AppDomain.CreateDomain(File, Nothing, PluginDomainSetup, PluginPermissions, TrustedAssembly)
    
            'Create an instance of the plugin.
            Dim Plugin As PluginBase = CType(PluginDomain.CreateInstanceFromAndUnwrap(FullPath, PluginType.FullName), PluginBase)
            Dim PluginInfo As New PluginInfo(FullPath, Plugin, PluginDomain)
    
            'Is a plugin by this name already loaded?
            If Me.IsPluginLoaded(Plugin.Name) = True Then
                Dim Name As String = Plugin.Name
                Me.UnloadPlugin(PluginInfo)
    
                Throw New ApplicationException("A plugin by the name """ & Name & """ is already loaded!")
            End If
    
            'Add the plugin to our lookup table and name list.
            Me.PluginLookup.Add(Plugin.Name, PluginInfo)
            Me.PluginList.Add(Plugin.Name)
    
            'Return the loaded plugin to the caller.
            Return PluginInfo
        End Function
    
        ''' <summary>
        ''' Unloads a plugin.
        ''' </summary>
        ''' <param name="Name">The name of the plugin to unload.</param>
        ''' <remarks></remarks>
        <SecurityCritical()>
        Public Sub UnloadPlugin(ByVal Name As String)
            Dim Plugin As PluginInfo = Me.GetPluginByName(Name)
            If Plugin Is Nothing Then Throw New ArgumentException("No plugin by the name """ & Name & """ is loaded.", "Name")
            Me.UnloadPlugin(Plugin)
        End Sub
    
        ''' <summary>
        ''' Unloads a plugin.
        ''' </summary>
        ''' <param name="PluginInfo">The plugin to unload.</param>
        ''' <remarks></remarks>
        <SecurityCritical()>
        Public Sub UnloadPlugin(ByVal PluginInfo As PluginInfo)
            If PluginInfo Is Nothing Then Throw New ArgumentNullException("PluginInfo")
            If PluginInfo.Unloaded = True Then Return
            Dim PluginName As String = PluginInfo.Plugin.Name
    
            Dim Permission As New SecurityPermission(SecurityPermissionFlag.ControlAppDomain)
            Permission.Assert()
    
            PluginInfo.Plugin.OnExit()
            PluginInfo.Unload()
            AppDomain.Unload(PluginInfo.AppDomain)
    
            CodeAccessPermission.RevertAssert()
    
            Me.PluginLookup.Remove(PluginName)
        End Sub
    
        ''' <summary>
        ''' Attempts to get a class derived from PluginBase in the specified assembly.
        ''' </summary>
        ''' <param name="PluginAssembly">The assembly to check.</param>
        ''' <remarks></remarks>
        <SecurityCritical()>
        Private Shared Function GetPluginType(ByVal PluginAssembly As Assembly) As Type
            For Each t As Type In PluginAssembly.GetTypes()
                If GetType(PluginBase).IsAssignableFrom(t) = True Then Return t
            Next
            Return Nothing
        End Function
    
        ''' <summary>
        ''' Initializes a new instance of the PluginManager class.
        ''' </summary>
        ''' <remarks></remarks>
        <SecurityCritical()>
        Public Sub New()
            Me.CurrentAppDomain = AppDomain.CurrentDomain
        End Sub
    
    #Region "IDisposable Support"
        Private disposedValue As Boolean ' To detect redundant calls
    
        ' IDisposable
        <SecurityCritical()>
        Protected Sub Dispose(disposing As Boolean)
            If Not Me.disposedValue Then
                If disposing Then
                    ' TODO: dispose managed state (managed objects).
    
                    'Unload all plugins.
                    For Each PluginPair As KeyValuePair(Of String, PluginInfo) In Me.PluginLookup
                        Try : Me.UnloadPlugin(PluginPair.Value) : Catch : End Try
                    Next
                End If
    
                ' TODO: free unmanaged resources (unmanaged objects) and override Finalize() below.
                ' TODO: set large fields to null.
            End If
            Me.disposedValue = True
        End Sub
    
        ' TODO: override Finalize() only if Dispose(ByVal disposing As Boolean) above has code to free unmanaged resources.
        'Protected Overrides Sub Finalize()
        '    ' Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
        '    Dispose(False)
        '    MyBase.Finalize()
        'End Sub
    
        ' This code added by Visual Basic to correctly implement the disposable pattern.
        <SecurityCritical()>
        Public Sub Dispose() Implements IDisposable.Dispose
            ' Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
            Dispose(True)
            GC.SuppressFinalize(Me)
        End Sub
    #End Region
    
    End Class
    
    Imports System.Windows.Forms
    Imports System.Security
    Imports System.Security.Permissions
    
    ''' <summary>
    ''' A class for unloading plugins from within their AppDomains.
    ''' </summary>
    ''' <remarks></remarks>
    <SecurityCritical()>
    Public NotInheritable Class PluginUnloader
        Inherits MarshalByRefObject
    
        ''' <summary>
        ''' Calls Application.Exit(). This must be called inside the plugin's AppDomain.
        ''' </summary>
        ''' <remarks></remarks>
        <SecurityCritical()>
        Public Sub Unload()
            'Request permission to execute managed code (required to call Application.Exit()).
            Dim Permission As New SecurityPermission(SecurityPermissionFlag.UnmanagedCode)
            Permission.Assert()
    
            'Exits the plugin's UI threads (if any exist).
            Application.Exit()
    
            'Revert UnmanagedCode privilege.
            CodeAccessPermission.RevertAssert()
        End Sub
    
        ''' <summary>
        ''' Initializes a new instance of the PluginUnloader class.
        ''' </summary>
        ''' <remarks></remarks>
        <SecurityCritical()>
        Public Sub New()
        End Sub
    End Class
    
    插件示例:

    Public Class TestPlugin
        Inherits PluginBase
    
        Public Overrides ReadOnly Property Name As String
            Get
                Return "Test Plugin"
            End Get
        End Property
    
        Public Overrides Sub DoSomething()
            Application.Run(New MyContext)
        End Sub
    
        Public Overrides Sub OnExit()
            'Do some cleanup here, if necessary.
            'No need to call Application.Exit() - it is called automatically by PluginUnloader.
        End Sub
    End Class
    

    权限如何工作 调用
    PluginManager.LoadPlugin()
    方法时,可以向其传递要加载的插件的路径,但也可以向其传递要应用于插件的一组权限(如果愿意,这是可选的)

    默认情况下,所有插件仅加载具有以下权限的

    • 它可以执行自己的托管代码

    • 它拥有对插件文件本身及其目录的读取权限

    这意味着插件:

    • 无法执行任何非托管(也称为本机)代码。例如,这是
      DllImport
      Declare函数
      声明

    • 无法读取/写入/创建/删除任何文件

    • 不能有用户界面(打开任何窗口,使用通知图标等)

    • 无法访问internet

    • 不能运行除自身和框架之外的任何代码(在其限制范围内)

    • ……等等,等等

    这可以通过指定加载插件时应授予插件的权限来更改。例如,如果您希望插件能够读取和写入特定目录中的文件,您可以执行以下操作:

    PluginManager.LoadPlugin("Plugins\TestPlugin.dll", 
        New FileIOPermission(FileIOPermissionAccess.AllAccess, "C:\some\folder"))
    
    或者,如果您希望它能够访问任何文件夹或文件:

    PluginManager.LoadPlugin("Plugins\TestPlugin.dll", 
        New FileIOPermission(PermissionState.Unrestricted))
    
    只需不断添加参数即可添加多个权限:

    PluginManager.LoadPlugin("Plugins\TestPlugin.dll", 
        New FileIOPermission(PermissionState.Unrestricted), 
        New UIPermission(UIPermissionWindow.AllWindows), 
        New WebPermission(PermissionState.Unrestricted))
    
    有关所有可用权限类型的列表,请参阅:

    尝试在单独的
    AppDomain
    中运行特定插件的代码。它应该使
    Application.Exit()
    只终止该应用程序,而不是终止整个应用程序。@VisualIncent,感谢您的回复,我试图按照链接中的示例进行操作,但我很抱歉