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

时间:2018-05-17 14:40:32

标签: vb.net

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

PluginContracts代码:

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

(插件应用是带有“MyContext”类中托盘图标的Dll)

我有一切正常,插件加载(使用托盘图标),但我无法关闭它并加载其他内容。 我有一个FileSystemWatcher将关闭插件,更新Dll然后重新打开它,但它关闭主应用程序,我不能做任何其他事情......

感谢您的帮助

1 个答案:

答案 0 :(得分:1)

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

不安全的方式

如果你的插件在所谓的完全信任中执行,这意味着它可以做任何想做的事情,那么你必须做三件事:

  1. 调整代码以在单独的AppDomain中启动插件。

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

  3. IPluginInterface更改为MustInherit课程。这是因为在不同AppDomains 必须之间编组的代码在继承自MarshalByRefObject的对象中运行。

  4. 新代码:

      

    编辑(2018-05-29)

         

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

         

    我已在下面的代码中快速实现了这一点。 关闭插件后,您现在必须致电PluginClass.Exit()而不是DoExit()

         

    经过一些测试后,我会将其添加到更安全的解决方案中。

    Dim PluginDomain As AppDomain = Nothing
    Dim PluginClass As PluginBase = Nothing
    
    Sub Main()
        LoadTray()
    
        If PluginClass IsNot Nothing Then
            PluginClass.WaitForExit()
        End If
    End Sub
    
    Sub LoadTray()
        Dim dll As Assembly = Assembly.LoadFrom(GetDLL.TrayDll)
        For Each t As Type In dll.GetTypes
            If GetType(PluginBase).IsAssignableFrom(t) = True Then
                Try
                    Dim PluginDomainSetup As New AppDomainSetup() With {
                        .ApplicationBase = AppDomain.CurrentDomain.BaseDirectory,
                        .PrivateBinPath = "Plugins"
                    }
    
                    PluginDomain = AppDomain.CreateDomain("MyPluginDomain", Nothing, PluginDomainSetup)
                    PluginClass = CType(PluginDomain.CreateInstanceFromAndUnwrap(GetDLL.TrayDll, t.FullName), PluginBase)
    
                    Dim PluginThread As New Thread(AddressOf PluginClass.DoSomething)
                    PluginThread.IsBackground = True
                    PluginThread.Start()
                Catch ex As Exception
                    MessageBox.Show(ex.ToString())
                End Try
    
                Exit For 'Don't forget to exit the loop.
            End If
        Next
    End Sub
    

    <强> 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
    

    然而 ,因为让插件以完全信任运行是非常不安全的我设计了一个解决方案,让您可以控制插件可以执行的操作。这要求我重写大部分代码,因为它需要一个不同的结构才能工作。

    安全(r)方式

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

    • PluginBase - 所有插件的基类。
    • PluginInfo - 包含有关插件信息的包装器(由PluginManager使用)。
    • PluginManager - 负责加载,卸载和跟踪插件的经理。
    • PluginUnloader - 在插件的限制AppDomain中完全信任的类。仅用于调用Application.Exit()

    首先,要使所有工作正常,PluginContracts DLL需要使用强名称进行签名。以下是如何做到的:

    1. 右键点击PluginContracts中的Solution Explorer项目,然后按Properties

    2. 选择Signing标签。

    3. 选中Sign the assembly复选框,离开Delay sign only 取消选中

    4. 打开下拉菜单,然后按<New...>

    5. 给密钥一个文件名和密码(请务必记住这一点!)。

    6. 完成!

    7. 既然已经修复了,您需要使PluginContracts DLL可以被部分受信任的代码调用。这样我们的插件就可以使用它,因为它将作为不受信任的代码运行

      1. 再次在Solution Explorer中选择项目。

      2. Solution ExplorerShow all files所示的按钮。

      3. 展开My Project节点。

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

        <Assembly: AllowPartiallyTrustedCallers(PartialTrustVisibilityLevel:=Security.PartialTrustVisibilityLevel.NotVisibleByDefault)> 
        
      5. 执行此操作有一个缺点:PluginContracts DLL中的所有代码现在都以相当低的权限运行。要使其再次使用标准权限运行,您必须使用SecurityCritical attribute装饰每个类PluginBase类除外(在下面的代码中我已经解决了这一切,所以你不需要改变任何东西)。因此,我建议您PluginContracts项目中有以下四个类:

        <强> PluginBase.vb:

        ''' <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
        

        <强> PluginInfo.vb:

        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
        

        <强> PluginManager.vb:

        '-------------------------------------------------------------------------------
        '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
        

        <强> PluginUnloader.vb:

        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
        


        使用示例

        主要代码(目前在表单中使用):

        Dim PluginManager As New PluginManager
        Dim PluginClass As PluginInfo
        
        Private Sub RunPluginButton_Click(sender As System.Object, e As System.EventArgs) Handles RunPluginButton.Click
            'Load our plugin and give it UI permissions.
            'The "UIPermissionWindow.AllWindows" flag allows our plugin to create a user interface (display windows, notify icons, etc.).
            PluginClass = PluginManager.LoadPlugin("Plugins\TestPlugin.dll", New UIPermission(UIPermissionWindow.AllWindows))
        
            'IMPORTANT: Each plugin must run in its own thread!
            Dim tr As New Thread(AddressOf PluginClass.Plugin.DoSomething)
            tr.IsBackground = True
            tr.Start()
        End Sub
        
        Private Sub ExitPluginButton_Click(sender As System.Object, e As System.EventArgs) Handles ExitPluginButton.Click
            If PluginClass Is Nothing Then
                MessageBox.Show("Plugin not loaded!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
                Return
            End If
        
            PluginManager.UnloadPlugin(PluginClass)
            PluginClass = Nothing
        End Sub
        

        示例插件:

        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()方法时,你传递了一个加载插件的路径,但你也可以传递一组你想要应用于插件的权限(如果你愿意,这个是可选的。

        默认情况下,所有插件都使用以下权限加载

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

        • 它具有对插件文件本身及其目录的读访问权。

        这意味着一个插件:

        • 无法执行任何非托管(也称为原生)代码。这是例如DllImportDeclare Function声明。

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

        • 无法拥有用户界面(打开任何窗口,使用通知图标等)。

        • 可以访问互联网。

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

        • ......等等......

        可以通过指定插件应该授予哪些权限,以及何时加载它来更改。例如,如果您希望您的插件能够读取和写入某个目录中的文件,您可以这样做:

        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))
        

        有关所有可用权限类型的列表,请参阅:
        https://msdn.microsoft.com/en-us/library/h846e9b3(v=vs.110).aspx