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


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

ms182161.collapse_all(ru-ru,VS.110).gifОписание

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

В следующем примере показан метод Interaction.Beep, создающий оболочку для функции MessageBeep из библиотеки user32.dll.P/Invoke MessageBeep находится внутри класса NativeMethods.

ms182161.collapse_all(ru-ru,VS.110).gifКод

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

ms182161.collapse_all(ru-ru,VS.110).gifОписание

Методы P/Invoke, которые могут быть безопасными при открытии любым приложением и не имеют каких-либо побочных эффектов, следует поместить в класс с именем SafeNativeMethods.При их вызове не нужно требовать разрешений и уделять им слишком много внимания.

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

ms182161.collapse_all(ru-ru,VS.110).gifКод

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

ms182161.collapse_all(ru-ru,VS.110).gifОписание

Методы P/Invoke, которые не являются безопасными при вызове и могут иметь побочные эффекты, следует поместить в класс с именем UnsafeNativeMethods.Эти методы должны быть тщательно проверены, чтобы исключить возможность случайного открытия пользователем.В этом случае целесообразно использовать правило CA2118: обзор использования SuppressUnmanagedCodeSecurityAttribute.В качестве альтернативы методы должны иметь другое разрешение, требуемое вместо UnmanagedCode при их использовании.

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

ms182161.collapse_all(ru-ru,VS.110).gifКод

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

См. также

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

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