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 Projektowanie
Poprawka łamiąca lub nienaruszająca Przełomowe
Domyślnie włączone na platformie .NET 10 Nie.
Zastosowane języki C# i Visual Basic

Przyczyna

Metoda używa usług wywoływania platformy 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 przechodzenia stosu dla uprawnień do niezarządzanego kodu. (System.Security.SuppressUnmanagedCodeSecurityAttribute nie powinien być stosowany do tej klasy). Ta klasa jest przeznaczona dla metod, które mogą być używane w dowolnym miejscu, ponieważ zostanie wykonane przechodzenie po stosie.

  • SafeNativeMethods — ta klasa blokuje wykonywanie przejść stosu dla uprawnień kodu niezarządzanego. (System.Security.SuppressUnmanagedCodeSecurityAttribute jest zastosowany do tej klasy). Ta klasa jest dla metod, które są bezpieczne do wywołania przez każdego. Wywołujący te metody nie muszą przeprowadzać pełnego przeglądu zabezpieczeń w celu upewnienia się, że ich użycie jest bezpieczne, ponieważ metody są nieszkodliwe dla każdego, kto je wywołuje.

  • UnsafeNativeMethods — ta klasa pomija przejścia 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, kto wywołuje te metody, musi przeprowadzić pełny przegląd zabezpieczeń, aby upewnić się, że użycie jest bezpieczne, ponieważ nie zostanie wykonane żadne sprawdzenie 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 przechodzenia stosu, aby upewnić się, że wszyscy wywołujący mają uprawnienia UnmanagedCode. Środowisko uruchomieniowe zwykle sprawdza to uprawnienie podczas uruchamiania. Ponieważ sprawdzanie nie jest przeprowadzane, może to znacznie poprawić wydajność wywołań 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 UnsafeNativeMethods

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

Zobacz też