CA1060:将 P/Invoke 移动到 NativeMethods 类

类型名

MovePInvokesToNativeMethodsClass

CheckId

CA1060

类别

Microsoft.Design

是否重大更改

原因

某方法使用平台调用服务访问非托管代码,该方法不是任何 NativeMethods 类的成员。

规则说明

平台调用方法(例如,用 DllImportAttribute 特性标记的方法,或者在 Visual Basic 中使用 Declare 关键字定义的方法)访问非托管代码。这些方法必须位于下列类之一中:

  • NativeMethods - 此类不取消对非托管代码权限的堆栈审核。(SuppressUnmanagedCodeSecurityAttribute 不能应用于此类。)由于将执行堆栈审核,因此此类适用于可在任意位置使用的方法。

  • SafeNativeMethods - 此类取消对非托管代码权限的堆栈审核。(SuppressUnmanagedCodeSecurityAttribute 应用于此类。)此类适用于任何人都可安全调用的方法。由于这些方法对于任何调用方来说都是无害的,因此这些方法的调用方无需进行全面的安全检查以确保使用的安全性。

  • UnsafeNativeMethods - 此类取消对非托管代码权限的堆栈审核。(SuppressUnmanagedCodeSecurityAttribute 应用于此类。)此类适用于存在潜在危险的方法。由于不会执行任何堆栈审核,因此这些方法的任何调用方都必须进行全面的安全检查以确保使用的安全性。

这些类声明为 internal(在 Visual Basic 中为 Friend),并声明一个私有构造函数以防止创建新的实例。这些类中的方法应为 static 和 internal(在 Visual Basic 中为 Shared 和 Friend)。

如何解决冲突

若要解决与此规则的冲突,请将方法移动到适当的 NativeMethods 类。对于大多数应用程序而言,将 P/Invokes 移动到名为 NativeMethods 的新类就足够了。

但是,如果您在开发要用于其他应用程序的库,则应考虑定义两个名为 SafeNativeMethodsUnsafeNativeMethods 的其他类。这些类与 NativeMethods 类相似,但它们用一个名为 SuppressUnmanagedCodeSecurityAttribute 的特殊特性来标记。应用此特性时,运行时不通过执行完整的堆栈审核来确保所有调用方都拥有 UnmanagedCode 权限。运行时通常在启动时检查此权限。由于不执行该检查,因此可以显著提高这些非托管方法的调用性能。并且,还允许具有有限权限的代码调用这些方法。

但是,您应谨慎使用此特性。如果它未正确实现,就可能有严重的安全含义。

有关如何实现这些方法的信息,请参见 NativeMethods 示例、SafeNativeMethods 示例和 UnsafeNativeMethods 示例。

何时禁止显示警告

不要禁止显示此规则发出的警告。

示例

下面的示例声明一个与该规则冲突的方法。若要更正该冲突,应将 RemoveDirectory P/Invoke 移动到设计为只存放 P/Invokes 的适当类。

Imports System

NameSpace MSInternalLibrary

' Violates rule: MovePInvokesToNativeMethodsClass. 
Friend Class UnmanagedApi
    Friend Declare Function RemoveDirectory Lib "kernel32" ( _
       ByVal Name As String) As Boolean 
End Class 

End NameSpace 
using System;
using System.Runtime.InteropServices;

namespace DesignLibrary
{
// Violates rule: MovePInvokesToNativeMethodsClass. 
    internal class UnmanagedApi
    {
        [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
        internal static extern bool RemoveDirectory(string name);
    }
}

NativeMethods 示例

说明

由于 NativeMethods 类不应使用 SuppressUnmanagedCodeSecurityAttribute 来标记,因此其中放置的 P/Invoke 将需要 UnmanagedCode 权限。由于大多数应用程序都从本地计算机中运行并在完全信任下一起运行,因此这通常不是问题。但是,如果您在开发可重用的库,则应考虑定义一个 SafeNativeMethodsUnsafeNativeMethods 类。

下面的示例演示从 user32.dll 中包装 MessageBeep 函数的 Interaction.Beep 方法。MessageBeep P/Invoke 放置在 NativeMethods 类中。

代码

Imports System    
Imports System.Runtime.InteropServices    
Imports System.ComponentModel         

Public NotInheritable Class Interaction  

    Private Sub New()        
    End Sub                 

    ' Callers require Unmanaged permission         
    Public Shared Sub Beep()                        
        ' No need to demand a permission as callers of Interaction.Beep                      
        ' will require UnmanagedCode permission                      
        If Not NativeMethods.MessageBeep(-1) Then                 
            Throw New Win32Exception()            
        End If 

    End Sub   

End Class          

Friend NotInheritable Class NativeMethods  

    Private Sub New()        
    End Sub             

    <DllImport("user32.dll", CharSet:=CharSet.Auto)> _        
    Friend Shared Function MessageBeep(ByVal uType As Integer) As <MarshalAs(UnmanagedType.Bool)> Boolean         
    End Function   

End Class
using System;    
using System.Runtime.InteropServices;    
using System.ComponentModel;              

public static class Interaction    
{        
    // Callers require Unmanaged permission         
    public static void Beep()           
    {            
        // No need to demand a permission as callers of Interaction.Beep             
        // will require UnmanagedCode permission             
        if (!NativeMethods.MessageBeep(-1))                
            throw new Win32Exception();        
    }    
}            

internal static class NativeMethods    
{        
    [DllImport("user32.dll", CharSet = CharSet.Auto)]        
    [return: MarshalAs(UnmanagedType.Bool)]        
    internal static extern bool MessageBeep(int uType);    
}

SafeNativeMethods 示例

说明

可以安全地向任何应用程序公开并且没有任何负面影响的 P/Invoke 方法应放置在名为 SafeNativeMethods 的类中。您无需要求权限,也无需过多关注调用位置。

下面的示例演示从 kernel32.dll 中包装 GetTickCount 函数的 Environment.TickCount 属性。

代码

Imports System   
Imports System.Runtime.InteropServices   
Imports System.Security       

Public NotInheritable Class Environment       

    Private Sub New()       
    End Sub            

    ' Callers do not require Unmanaged permission        
    Public Shared ReadOnly Property TickCount() As Integer            
        Get                
            ' No need to demand a permission in place of                
            ' UnmanagedCode as GetTickCount is considered                
            ' a safe method                
            Return SafeNativeMethods.GetTickCount()               
        End Get        
    End Property        

End Class       

<SuppressUnmanagedCodeSecurityAttribute()> _   
Friend NotInheritable Class SafeNativeMethods             

    Private Sub New()       
    End Sub           

    <DllImport("kernel32.dll", CharSet:=CharSet.Auto, ExactSpelling:=True)> _       
    Friend Shared Function GetTickCount() As Integer        
    End Function        

End Class
using System;   
using System.Runtime.InteropServices;   
using System.Security;  

public static class Environment   
{       
    // Callers do not require UnmanagedCode permission        
    public static int TickCount        
    {           
        get           
        {              
            // No need to demand a permission in place of                
            // UnmanagedCode as GetTickCount is considered               
            // a safe method               
            return SafeNativeMethods.GetTickCount();           
        }       
    }   
}            

[SuppressUnmanagedCodeSecurityAttribute]   
internal static class SafeNativeMethods   
{       
    [DllImport("kernel32.dll", CharSet=CharSet.Auto, ExactSpelling=true)]       
    internal static extern int GetTickCount();   
}

UnsafeNativeMethods 示例

说明

无法安全地调用而且可能造成负面影响的 P/Invoke 方法应放置在名为 UnSafeNativeMethods 的类中。应对这些方法进行严格检查,以确保未在无意中向用户公开这些方法。CA2118:检查 SuppressUnmanagedCodeSecurityAttribute 用法这条规则对此有所帮助。或者,在使用这些方法时,它们应该具有代替 UnmanagedCode 要求的另一个权限。

下面的示例演示从 user32.dll 中包装 ShowCursor 函数的 Cursor.Hide 方法。

代码

Imports System   
Imports System.Runtime.InteropServices   
Imports System.Security   
Imports System.Security.Permissions       

Public NotInheritable Class Cursor           

    Private Sub New()       
    End Sub            

    ' Callers do not require Unmanaged permission, however,          
    ' they do require UIPermission.AllWindows        
    Public Shared Sub Hide()                 
        ' Need to demand an appropriate permission                    
        ' in  place of UnmanagedCode permission as                     
        ' ShowCursor is not considered a safe method                    
        Dim permission As New UIPermission(UIPermissionWindow.AllWindows)           
        permission.Demand()           
        UnsafeNativeMethods.ShowCursor(False)                

    End Sub        

End Class       

<SuppressUnmanagedCodeSecurityAttribute()> _   
Friend NotInheritable Class UnsafeNativeMethods           

    Private Sub New()       
    End Sub           

    <DllImport("user32.dll", CharSet:=CharSet.Auto, ExactSpelling:=True)> _       
    Friend Shared Function ShowCursor(<MarshalAs(UnmanagedType.Bool)> ByVal bShow As Boolean) As Integer        
    End Function        

End Class
using System;   
using System.Runtime.InteropServices;   
using System.Security;   
using System.Security.Permissions;           

public static class Cursor   
{       
    // Callers do not require UnmanagedCode permission, however,        
    // they do require UIPermissionWindow.AllWindows        
    public static void Hide()          
    {           
        // Need to demand an appropriate permission            
        // in  place of UnmanagedCode permission as             
        // ShowCursor is not considered a safe method            
        new UIPermission(UIPermissionWindow.AllWindows).Demand();           
        UnsafeNativeMethods.ShowCursor(false);       
    }   
}            

[SuppressUnmanagedCodeSecurityAttribute]   
internal static class UnsafeNativeMethods   
{       
    [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]       
    internal static extern int ShowCursor([MarshalAs(UnmanagedType.Bool)]bool bShow);   
}

请参见

其他资源

设计警告