Поделиться через


CA1060: переместите P/Invokes в класс NativeMethods

TypeName

MovePInvokesToNativeMethodsClass

CheckId

CA1060

Категория

Microsoft.Design

Критическое изменение

Критическое изменение

Причина

Метод использует службы PInvoke для доступа к неуправляемому коду и не является членом одного из классов NativeMethods.

Описание правила

Методы PInvoke, помеченные атрибутом System.Runtime.InteropServices.DllImportAttribute (или методы, определенные с помощью ключевого слова Declare в Visual Basic), имеют доступ к неуправляемому коду. Методы должны находиться в одном из следующих классов.

  • NativeMethods — этот класс не подавляет проверку стека на разрешение неуправляемого кода. (к классу не должен быть применен System.Security.SuppressUnmanagedCodeSecurityAttribute.) Этот класс предназначен для методов, которые могут быть использованы где угодно, поскольку выполняется проверка стека.

  • SafeNativeMethods — этот класс подавляет проверку стека на разрешение неуправляемого кода. (к классу применен System.Security.SuppressUnmanagedCodeSecurityAttribute.) Класс предназначен для методов, которые являются безопасными для всех, кто их вызывает. Для вызывающих их методов нет необходимости в выполнении полной проверки безопасности использования, поскольку они являются безопасными для любого вызывающего их метода.

  • UnsafeNativeMethods — этот класс подавляет проверку стека на разрешение неуправляемого кода. (к классу применен System.Security.SuppressUnmanagedCodeSecurityAttribute.) Класс предназначен для методов, которые являются потенциально опасными. Все вызывающие их методы должны пройти полную проверку безопасности использования, поскольку проверка стека не выполняется.

Эти классы объявлены как internal (Friend в Visual Basic) и объявляют закрытый конструктор для предотвращения создания новых экземпляров. Методы в этих классах должны быть static и internal (Shared и Friend в Visual Basic).

Устранение нарушений

Чтобы устранить нарушение данного правила, переместите метод в соответствующий класс NativeMethods. Для большинства приложений достаточно переместить P/Invokes в новый класс с именем NativeMethods.

Однако при разработке библиотек для использования в других приложениях нужно определить два дополнительных класса с именами SafeNativeMethods и UnsafeNativeMethods. Эти классы похожи на класс NativeMethods, но они помечены специальным атрибутом SuppressUnmanagedCodeSecurityAttribute. При применении этого атрибута среда выполнения не осуществляет полную проверку стека на наличие у всех вызывающих методов разрешения UnmanagedCode. Обычно такая проверка производится средой выполнения во время запуска. Поскольку проверка не выполняется, производительность для вызовов неуправляемых методов может значительно улучшиться и, кроме того, код с ограниченными разрешениями может вызывать эти методы.

Тем не менее этот атрибут следует использовать с большой осторожностью. Он может иметь последствия для безопасности, если реализуется неправильно.

Сведения о способах реализации методов см. в примерах NativeMethods, SafeNativeMethods и UnsafeNativeMethods.

Отключение предупреждений

Для этого правила отключать вывод предупреждений не следует.

Пример

В следующем примере объявляется метод, нарушающий это правило. Чтобы устранить нарушение, P/Invoke RemoveDirectory следует переместить в соответствующий класс, разработанный исключительно для размещения P/Invoke.

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. Так как большинство приложений выполняется на локальном компьютере с полным доверием, обычно это не является проблемой. Однако при разработке многократно используемых библиотек нужно определить класс SafeNativeMethods или UnsafeNativeMethods.

В следующем примере показан метод Interaction.Beep, создающий оболочку для функции MessageBeep из библиотеки user32.dll. P/Invoke MessageBeep находится внутри класса 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. При их вызове не нужно требовать разрешений и уделять им слишком много внимания.

В следующем примере показано свойство Environment.TickCount, создающее оболочку для функции GetTickCount из библиотеки kernel32.dll.

Код

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 при их использовании.

В следующем примере показан метод Cursor.Hide, создающий оболочку для функции ShowCursor из библиотеки user32.dll.

Код

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

См. также

Другие ресурсы

Предупреждения конструктора