CA1060: переместите P/Invokes в класс NativeMethods
Свойство | Значение |
---|---|
Идентификатор правила | CA1060 |
Заголовок | Переместите методы P/Invoke в класс NativeMethods |
Категория | Проектирование |
Исправление является критическим или не критическим | Критическое |
Включен по умолчанию в .NET 9 | No |
Причина
Метод использует службы вызова платформы для доступа к неуправляемому коду и не является членом одного из классов 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/Invoke в новый класс с именем NativeMethods.
Однако при разработке библиотек для использования в других приложениях следует рассмотреть возможность определения двух других классов, которые называются SafeNativeMethods и UnsafeNativeMethods. Эти классы похожи на класс NativeMethods. Однако они помечаются с помощью специального атрибута с именем SuppressUnmanagedCodeSecurityAttribute. При применении этого атрибута среда выполнения не выполняет полный обход стека, чтобы убедиться, что все вызывающие объекты имеют разрешение UnmanagedCode. Среда выполнения обычно проверяет наличие этого разрешения при запуске. Так как проверка не выполняется, она может значительно повысить производительность вызовов этих неуправляемых методов. Он также позволяет коду с ограниченными разрешениями вызывать эти методы.
Однако этот атрибут следует использовать с большой осторожностью. Он может иметь серьезные последствия для безопасности, если он реализован неправильно.
Сведения о том, как реализовать методы, см. в примере NativeMethods, примере SafeNativeMethods и примере UnsafeNativeMethods.
Когда лучше отключить предупреждения
Для этого правила отключать вывод предупреждений не следует.
Пример
В следующем примере объявляется метод, нарушающий это правило. Чтобы устранить нарушение, RemoveDirectory P/Invoke следует переместить в соответствующий класс, предназначенный для хранения только P/Invoke.
' Violates rule: MovePInvokesToNativeMethodsClass.
Friend Class UnmanagedApi
Friend Declare Function RemoveDirectory Lib "kernel32" (
ByVal Name As String) As Boolean
End Class
// 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. MessageBeep P/Invoke помещается в класс NativeMethods.
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
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.
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
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. Эти методы должны быть тщательно проверены, чтобы исключить случайно предоставление доступа пользователю.
В следующем примере показан метод Cursor.Hide, который заключает в оболочку функцию ShowCursor из user32.dll.
Public NotInheritable Class Cursor
Private Sub New()
End Sub
Public Shared Sub Hide()
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
public static class Cursor
{
public static void Hide()
{
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);
}