CA1060: Spostare i P/Invoke nella classe NativeMethods

Proprietà valore
ID regola CA1060
Titolo Spostare P/Invoke nella classe NativeMethods
Categoria Progettazione
Correzione che causa un'interruzione o un'interruzione Interruzione
Abilitato per impostazione predefinita in .NET 8 No

Causa

Un metodo usa Platform Invocation Services per accedere a codice non gestito e non è membro di una delle classi NativeMethods .

Descrizione regola

Metodi di chiamata della piattaforma, ad esempio quelli contrassegnati tramite l'attributo System.Runtime.InteropServices.DllImportAttribute o i metodi definiti tramite la Declare parola chiave in Visual Basic, accedono al codice non gestito. Questi metodi devono trovarsi in una delle classi seguenti:

  • NativeMethods : questa classe non elimina le procedure dello stack per l'autorizzazione del codice non gestito. (System.Security.SuppressUnmanagedCodeSecurityAttribute non deve essere applicato a questa classe. Questa classe è per i metodi che possono essere usati ovunque, perché verrà eseguita una procedura dettagliata dello stack.

  • Cassaforte NativeMethods: questa classe elimina le istruzioni dello stack per l'autorizzazione del codice non gestito. (System.Security.SuppressUnmanagedCodeSecurityAttribute viene applicato a questa classe. Questa classe è per i metodi sicuri per chiunque possa chiamare. I chiamanti di questi metodi non sono tenuti a eseguire una revisione completa della sicurezza per assicurarsi che l'utilizzo sia sicuro perché i metodi sono innocui per qualsiasi chiamante.

  • UnsafeNativeMethods : questa classe elimina le istruzioni dello stack per l'autorizzazione del codice non gestito. (System.Security.SuppressUnmanagedCodeSecurityAttribute viene applicato a questa classe. Questa classe è per i metodi potenzialmente pericolosi. Qualsiasi chiamante di questi metodi deve eseguire una revisione completa della sicurezza per assicurarsi che l'utilizzo sia sicuro perché non verrà eseguita alcuna procedura dettagliata dello stack.

Queste classi vengono dichiarate come internal (Friend in Visual Basic) e dichiarano un costruttore privato per impedire la creazione di nuove istanze. I metodi in queste classi devono essere static e internal (Shared e Friend in Visual Basic).

Come correggere le violazioni

Per correggere una violazione di questa regola, spostare il metodo nella classe NativeMethods appropriata. Per la maggior parte delle applicazioni, lo spostamento di P/Invoke in una nuova classe denominata NativeMethods è sufficiente.

Tuttavia, se si sviluppano librerie da usare in altre applicazioni, è consigliabile definire due altre classi denominate Cassaforte NativeMethods e UnsafeNativeMethods. Queste classi sono simili alla classe NativeMethods , ma sono contrassegnate usando un attributo speciale denominato SuppressUnmanagedCodeSecurityAttribute. Quando viene applicato questo attributo, il runtime non esegue una procedura completa dello stack per assicurarsi che tutti i chiamanti dispongano dell'autorizzazione UnmanagedCode . Il runtime controlla in genere questa autorizzazione all'avvio. Poiché il controllo non viene eseguito, può migliorare notevolmente le prestazioni per le chiamate a questi metodi non gestiti. Abilita anche il codice con autorizzazioni limitate per chiamare questi metodi.

Tuttavia, è consigliabile usare questo attributo con molta attenzione. Può avere gravi implicazioni sulla sicurezza se viene implementata in modo non corretto.

Per informazioni su come implementare i metodi, vedere l'esempio NativeMethods, Cassaforte NativeMethods di esempio e UnsafeNativeMethods.

Quando eliminare gli avvisi

Non escludere un avviso da questa regola.

Esempio

Nell'esempio seguente viene dichiarato un metodo che viola questa regola. Per correggere la violazione, il metodo RemoveDirectory P/Invoke deve essere spostato in una classe appropriata progettata per contenere solo 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);
}

Esempio di NativeMethods

Poiché la classe NativeMethods non deve essere contrassegnata tramite SuppressUnmanagedCodeSecurityAttribute, I P/Invoke inseriti richiedono l'autorizzazione UnmanagedCode . Poiché la maggior parte delle applicazioni viene eseguita dal computer locale ed eseguita insieme con attendibilità totale, questo in genere non è un problema. Tuttavia, se si sviluppano librerie riutilizzabili, è consigliabile definire una classe Cassaforte NativeMethods o UnsafeNativeMethods.

Nell'esempio seguente viene illustrato un metodo Interaction.Beep che esegue il wrapping della funzione MessageBeep da user32.dll. MessageBeep P/Invoke viene inserito nella classe 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);
}

Esempio di Cassaforte NativeMethods

I metodi P/Invoke che possono essere esposti in modo sicuro a qualsiasi applicazione e che non hanno effetti collaterali devono essere inseriti in una classe denominata Cassaforte NativeMethods. Non è necessario prestare molta attenzione a dove vengono chiamati.

L'esempio seguente mostra una proprietà Environment.TickCount che esegue il wrapping della funzione GetTickCount da 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();
}

Esempio unsafeNativeMethods

I metodi P/Invoke che non possono essere chiamati in modo sicuro e che potrebbero causare effetti collaterali devono essere inseriti in una classe denominata UnsafeNativeMethods. Questi metodi devono essere controllati rigorosamente per assicurarsi che non siano esposti all'utente involontariamente.

Nell'esempio seguente viene illustrato un metodo Cursor.Hide che esegue il wrapping della funzione ShowCursor da 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);
}

Vedi anche