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