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