CA1060: Przenieś P/Invokes do klasy NativeMethods

Właściwości Wartość
Identyfikator reguły CA1060
Stanowisko Przenieś metody P/Invoke do klasy NativeMethods
Kategoria Projekt
Poprawka powodująca niezgodność lub niezgodność Kluczowa
Domyślnie włączone na platformie .NET 8 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.

  • Sejf NativeMethods — 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 Sejf NativeMethods 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, Sejf NativeMethods przykład 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 Sejf NativeMethods lub UnsafeNativeMethods.

W poniższym przykładzie przedstawiono metodę Interaction.Beep, która opakowuje funkcję MessageBeep z pliku 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 Sejf NativeMethods

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 Sejf NativeMethods. 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 pliku 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 pliku 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ż