CA1060: Przenieś P/Invokes do klasy NativeMethods
Właściwości | Wartość |
---|---|
Identyfikator reguły | CA1060 |
Tytuł | Przenieś metody P/Invoke do klasy NativeMethods |
Kategoria | Projekt |
Poprawka powodująca niezgodność lub niezgodność | Kluczowa |
Domyślnie włączone na platformie .NET 9 | Nie. |
Przyczyna
Metoda używa usług Invocation Services platform do uzyskiwania dostępu do kodu niezarządzanego i nie jest członkiem jednej z klas NativeMethods .
Opis reguły
Metody wywołania platformy, takie jak metody oznaczone za pomocą atrybutu System.Runtime.InteropServices.DllImportAttribute lub metody zdefiniowane za pomocą słowa kluczowego Declare
w języku Visual Basic, uzyskują dostęp do niezarządzanych kodów. Te metody powinny znajdować się w jednej z następujących klas:
NativeMethods — ta klasa nie pomija spacerów stosu dla niezarządzanych uprawnień kodu. (System.Security.SuppressUnmanagedCodeSecurityAttribute nie może być stosowany do tej klasy). Ta klasa dotyczy metod, które mogą być używane w dowolnym miejscu, ponieważ zostanie wykonany przewodnik stosu.
SafeNativeMethods — ta klasa pomija przewodniki stosu dla niezarządzanych uprawnień kodu. (System.Security.SuppressUnmanagedCodeSecurityAttribute jest stosowany do tej klasy). Ta klasa jest dla metod, które są bezpieczne dla każdego, kto ma wywołać. Wywołania tych metod nie są wymagane do przeprowadzenia pełnego przeglądu zabezpieczeń, aby upewnić się, że użycie jest bezpieczne, ponieważ metody są nieszkodliwe dla każdego obiektu wywołującego.
UnsafeNativeMethods — ta klasa pomija przewodniki stosu dla niezarządzanych uprawnień kodu. (System.Security.SuppressUnmanagedCodeSecurityAttribute jest stosowany do tej klasy). Ta klasa dotyczy metod, które są potencjalnie niebezpieczne. Każdy obiekt wywołujący te metody musi przeprowadzić pełny przegląd zabezpieczeń, aby upewnić się, że użycie jest bezpieczne, ponieważ nie zostanie wykonany żaden przewodnik stosu.
Te klasy są deklarowane jako internal
(Friend
w Visual Basic) i deklarują konstruktor prywatny, aby zapobiec tworzeniu nowych wystąpień. Metody w tych klasach powinny mieć wartość static
i internal
(Shared
i Friend
w Visual Basic).
Jak naprawić naruszenia
Aby naprawić naruszenie tej reguły, przenieś metodę do odpowiedniej klasy NativeMethods . W przypadku większości aplikacji wystarczy przeniesienie operacji P/Invoke do nowej klasy o nazwie NativeMethods .
Jeśli jednak tworzysz biblioteki do użycia w innych aplikacjach, rozważ zdefiniowanie dwóch innych klas o nazwie SafeNativeMethods i UnsafeNativeMethods. Te klasy przypominają klasę NativeMethods , jednak są one oznaczone za pomocą specjalnego atrybutu o nazwie SuppressUnmanagedCodeSecurityAttribute. Po zastosowaniu tego atrybutu środowisko uruchomieniowe nie wykonuje pełnego przewodnika stosu, aby upewnić się, że wszystkie osoby wywołujące mają uprawnienie UnmanagedCode . Środowisko uruchomieniowe zwykle sprawdza to uprawnienie podczas uruchamiania. Ponieważ sprawdzanie nie jest wykonywane, może znacznie poprawić wydajność wywołań do tych niezarządzanych metod. Umożliwia również kod, który ma ograniczone uprawnienia do wywoływania tych metod.
Należy jednak użyć tego atrybutu z wielką starannością. Może to mieć poważne konsekwencje dla bezpieczeństwa, jeśli jest on implementowany nieprawidłowo.
Aby uzyskać informacje o sposobie implementowania metod, zobacz przykład NativeMethods , przykład SafeNativeMethods i UnsafeNativeMethods .
Kiedy pomijać ostrzeżenia
Nie pomijaj ostrzeżeń dla tej reguły.
Przykład
Poniższy przykład deklaruje metodę, która narusza tę regułę. Aby rozwiązać ten błąd, należy przenieść element RemoveDirectory P/Invoke do odpowiedniej klasy, która jest przeznaczona do przechowywania tylko wywołań 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);
}
Przykład nativeMethods
Ponieważ klasa NativeMethods nie powinna być oznaczona przy użyciu atrybutu SuppressUnmanagedCodeSecurityAttribute, wprowadzone w nim elementy P/Invoke będą wymagać uprawnienia UnmanagedCode. Ponieważ większość aplikacji działa z komputera lokalnego i działa razem z pełnym zaufaniem, zwykle nie jest to problem. Jeśli jednak tworzysz biblioteki wielokrotnego użytku, rozważ zdefiniowanie klasy SafeNativeMethods lub UnsafeNativeMethods .
W poniższym przykładzie przedstawiono metodę Interaction.Beep, która opakowuje funkcję MessageBeep z user32.dll. Element MessageBeep P/Invoke jest umieszczany w klasie 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);
}
Przykład safeNativeMethods
Metody P/Invoke, które mogą być bezpiecznie narażone na dowolną aplikację i które nie mają żadnych skutków ubocznych, należy umieścić w klasie o nazwie SafeNativeMethods. Nie musisz zwracać dużej uwagi na to, skąd są wywoływane.
W poniższym przykładzie przedstawiono właściwość Environment.TickCount, która opakowuje funkcję GetTickCount z 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();
}
Przykład niebezpiecznyNativeMethods
Metody P/Invoke, których nie można bezpiecznie wywołać i które mogą powodować skutki uboczne, należy umieścić w klasie o nazwie UnsafeNativeMethods. Te metody powinny być rygorystycznie sprawdzane, aby upewnić się, że nie są one widoczne dla użytkownika przypadkowo.
W poniższym przykładzie przedstawiono metodę Cursor.Hide , która opakowuje funkcję ShowCursor z 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);
}