CA1060 : Déplacer les P/Invoke vers une classe NativeMethods

Propriété Value
Identificateur de la règle CA1060
Titre Déplacer les P/Invoke vers une classe NativeMethods
Catégorie Conception
Le correctif est cassant ou non cassant Rupture
Activé par défaut dans .NET 8 Non

Cause

Une méthode utilise Platform Invocation Services pour accéder à du code non managé, et n’est pas membre de l’une des classes NativeMethods.

Description de la règle

Les méthodes d’appel de code non managé, telles que celles qui sont marquées avec l’attribut System.Runtime.InteropServices.DllImportAttribute, ou les méthodes définies à l’aide du mot clé Declare en Visual Basic, accèdent à du code non managé. Ces méthodes doivent être dans l’une des classes suivantes :

  • NativeMethods : cette classe ne supprime pas les parcours de pile pour l’autorisation de code non managé. (System.Security.SuppressUnmanagedCodeSecurityAttribute ne doit pas être appliqué à cette classe.) Cette classe est destinée aux méthodes qui peuvent être utilisées n’importe où, car un parcours de pile sera effectué.

  • SafeNativeMethods : cette classe supprime les parcours de pile pour l’autorisation de code non managé. (System.Security.SuppressUnmanagedCodeSecurityAttribute est appliqué à cette classe.) Cette classe est destinée aux méthodes que tout le monde peut appeler sans risque. Les appelants de ces méthodes ne sont pas tenus d’effectuer une révision de sécurité complète pour s’assurer que l’utilisation est sécurisée, car les méthodes sont inoffensives pour tout appelant.

  • UnsafeNativeMethods : cette classe supprime les parcours de pile pour l’autorisation de code non managé. (System.Security.SuppressUnmanagedCodeSecurityAttribute est appliqué à cette classe.) Cette classe est destinée aux méthodes potentiellement dangereuses. Tout appelant de ces méthodes doit effectuer une révision de sécurité complète pour s’assurer que l’utilisation est sécurisée, car aucun parcours de pile ne sera effectué.

Ces classes sont déclarées comme internal (Friend en Visual Basic), et déclarent un constructeur privé pour empêcher la création de nouvelles instances. Les méthodes de ces classes doivent être static et internal (Shared et Friend en Visual Basic).

Comment corriger les violations

Pour corriger une violation de cette règle, déplacez la méthode vers la classe NativeMethods appropriée. Pour la plupart des applications, le déplacement de P/Invokes vers une nouvelle classe nommée NativeMethods est suffisant.

Toutefois, si vous développez des bibliothèques pour une utilisation dans d’autres applications, vous devez envisager de définir deux autres classes nommées SafeNativeMethods et UnsafeNativeMethods. Ces classes ressemblent à la classe NativeMethods ; toutefois, elles sont marquées à l’aide d’un attribut spécial nommé SuppressUnmanagedCodeSecurityAttribute. Lorsque cet attribut est appliqué, le runtime n’effectue pas de parcours complet de la pile pour s’assurer que tous les appelants disposent de l’autorisation UnmanagedCode. Le runtime recherche généralement cette autorisation au démarrage. La vérification n’étant pas effectuée, cela peut considérablement améliorer les performances pour les appels à ces méthodes non managées. Cela permet également au code disposant d’autorisations limitées d’appeler ces méthodes.

Toutefois, vous devez utiliser cet attribut avec beaucoup de précaution. Il peut avoir de graves implications en matière de sécurité s’il est implémenté de manière incorrecte.

Pour plus d’informations sur l’implémentation des méthodes, consultez l’exemple NativeMethods, l’exemple SafeNativeMethods et l’exemple UnsafeNativeMethods.

Quand supprimer les avertissements

Ne supprimez aucun avertissement de cette règle.

Exemple

L’exemple suivant déclare une méthode qui enfreint cette règle. Pour corriger la violation, le P/Invoke removeDirectory doit être déplacé vers une classe appropriée conçue pour contenir uniquement des P/Invokes.

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

Exemple NativeMethods

Étant donné que la classe NativeMethods ne doit pas être marquée à l’aide de SuppressUnmanagedCodeSecurityAttribute, les P/Invokes qui y sont placés nécessitent l’autorisation UnmanagedCode. Étant donné que la plupart des applications s’exécutent à partir de l’ordinateur local et s’exécutent avec une confiance totale, ce n’est généralement pas un problème. Toutefois, si vous développez des bibliothèques réutilisables, vous devez envisager de définir une classe SafeNativeMethods ou UnsafeNativeMethods.

L’exemple suivant montre une méthode Interaction.Beep qui encapsule la fonction MessageBeep de user32.dll. Le P/Invoke MessageBeep est placé dans la 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);
}

Exemple SafeNativeMethods

Les méthodes P/Invoke qui peuvent être exposées sans risque à n’importe quelle application et qui n’ont aucun effet secondaire doivent être placées dans une classe nommée SafeNativeMethods. Vous n’avez pas à prêter beaucoup d’attention à l’endroit d’où elles sont appelées.

L’exemple suivant montre une propriété Environment.TickCount qui encapsule la fonction GetTickCount de 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();
}

Exemple UnsafeNativeMethods

Les méthodes P/Invoke qui ne peuvent pas être appelées sans risque et qui peuvent entraîner des effets secondaires doivent être placées dans une classe nommée UnsafeNativeMethods. Ces méthodes doivent être rigoureusement vérifiées pour s’assurer qu’elles ne sont pas exposées involontairement à l’utilisateur.

L’exemple suivant montre une méthode Cursor.Hide qui encapsule la fonction ShowCursor de 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);
}

Voir aussi