首页 > 解决方案 > 在不关闭整个应用程序的情况下无法关闭/卸载插件

问题描述

我需要一些关于我的应用程序的 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

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

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

谢谢您的帮助

标签: vb.net

解决方案


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

不安全的方式

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

  1. 调整您的代码以在单独的AppDomain.

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

  3. IPlugin一个类Interface变成一个MustInherit类。这是因为在不同之间编组的代码AppDomains 必须在继承自MarshalByRefObject.

新代码:

编辑(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()

首先也是最重要的,为了让所有工作正常运行,PluginContractsDLL 需要使用强名称进行签名。这是如何做到的:

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

  2. 选择Signing选项卡。

  3. Sign the assembly选中表示不Delay sign only 选中的复选框。

  4. 打开下拉菜单并按<New...>

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

  6. 完毕!

现在已经解决了,您需要使PluginContractsDLL 可供部分受信任的代码调用。这样我们的插件就可以使用它,因为它将作为不受信任的代码运行。

  1. 再次选择项目Solution Explorer

  2. 按下上面Solution Explorer写着的按钮Show all files

  3. 展开My Project节点。

  4. 双击AssemblyInfo.vb编辑它并在文件末尾添加这一行:

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

这样做有一个弊端:PluginContractsDLL 中的所有代码现在都将以相当低的权限运行。为了让它再次以标准权限运行,你必须用属性装饰每个类,除了类(在下面的代码中,我已经修复了所有这些,所以你不需要更改任何东西)。因此,我建议您在项目中只有以下四个类:SecurityCriticalPluginBasePluginContracts

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

插件信息.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

插件管理器.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

插件卸载程序.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


推荐阅读