CA1060 : Déplacer les P/Invoke vers une classe NativeMethods
TypeName |
MovePInvokesToNativeMethodsClass |
CheckId |
CA1060 |
Catégorie |
Microsoft.CSharp |
Modification avec rupture |
Oui |
Cause
Une méthode utilise des services d'appel de code non managé pour accéder à du code non managé et n'est membre d'aucune 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 se trouver dans l'une des classes suivantes :
NativeMethods - Cette classe ne supprime aucun 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 ne peuvent pas être utilisées n'importe où, du fait de l'exécution d'un parcours de pile.
SafeNativeMethods - Cette classe supprime des 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 qui garantissent la sécurité de tous les appelants. Les appelants de ces méthodes ne sont pas contraints de procéder à une révision de sécurité complète pour garantir la sécurisation de leur utilisation car elles ne présentent de danger pour aucun appelant.
UnsafeNativeMethods - Cette classe supprime des parcours de pile pour l'autorisation de code non managé. (System.Security.SuppressUnmanagedCodeSecurityAttribute est appliqué à cette classe.) Cette classe est destinée à des méthodes potentiellement dangereuses. Tout appelant de ces méthodes doit procéder à une révision de sécurité complète pour garantir la sécurisation de leur utilisation, car aucun parcours de pile ne sera effectué.
Ces classes sont déclarées en tant que internal (Friend en Visual Basic) et déclarent un constructeur privé pour empêcher la création de nouvelles instances. Les méthodes présentes dans 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, placez la méthode dans la classe NativeMethods appropriée. Pour la plupart des applications, le fait de déplacer les P/Invoke vers une classe nouvelle 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 appelées SafeNativeMethods et UnsafeNativeMethods. Ces classes ressemblent à la classe NativeMethods. Toutefois, elles sont marquées avec un attribut spécial appelé SuppressUnmanagedCodeSecurityAttribute. Lorsque cet attribut est appliqué, l'exécution n'effectue pas de parcours complet de la pile afin de s'assurer que tous les appelants ont l'autorisation UnmanagedCode. Le runtime vérifie habituellement la présence de cette autorisation au démarrage. Dans la mesure où le contrôle n'est pas effectué, cela peut améliorer grandement la performance pour les appels à ces méthodes non managées, et permet également au code avec des autorisations limitées d'appeler ces méthodes.
Cependant, vous devez utiliser cet attribut avec précaution. Les conséquences au niveau de la sécurité peuvent être sérieuses s'il est implémenté de manière incorrecte.
Pour plus d'informations sur l'implémentation de ces méthodes, consultez les exemples NativeMethods, SafeNativeMethods et UnsafeNativeMethods.
Quand supprimer les avertissements
Ne supprimez aucun avertissement de cette règle.
Exemple
L'exemple suivant déclare une méthode qui ne respecte pas cette règle. Pour corriger la violation, le P/Invoke RemoveDirectory doit être déplacé vers une classe appropriée destinée à contenir uniquement les P/Invoke.
Imports System
NameSpace MSInternalLibrary
' Violates rule: MovePInvokesToNativeMethodsClass.
Friend Class UnmanagedApi
Friend Declare Function RemoveDirectory Lib "kernel32" ( _
ByVal Name As String) As Boolean
End Class
End NameSpace
using System;
using System.Runtime.InteropServices;
namespace DesignLibrary
{
// Violates rule: MovePInvokesToNativeMethodsClass.
internal class UnmanagedApi
{
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
internal static extern bool RemoveDirectory(string name);
}
}
Exemple NativeMethods
Description
Dans la mesure où la classe NativeMethods ne doit pas être marquée avec SuppressUnmanagedCodeSecurityAttribute, les P/Invoke qui y sont placés nécessiteront l'autorisation UnmanagedCode. Comme la plupart des applications s'exécutent ensemble à partir de l'ordinateur local et avec la confiance totale, ceci ne pose généralement pas 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 affiche une méthode Interaction.Beep qui encapsule la fonction MessageBeep à partir d'user32.dll. P/Invoke de MessageBeep est placé dans la classe NativeMethods.
Code
Imports System
Imports System.Runtime.InteropServices
Imports System.ComponentModel
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
using System;
using System.Runtime.InteropServices;
using System.ComponentModel;
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
Description
Les méthodes P/Invoke qui peuvent être exposées sans risque à toute application et qui n'ont pas d'effets secondaires doivent être placées dans une classe nommée SafeNativeMethods. Vous n'avez pas à demander des autorisations et vous n'avez pas à faire trop attention à l'emplacement à partir duquel elles sont appelées.
L'exemple suivant affiche une propriété Environment.TickCount qui encapsule la fonction GetTickCount à partir de kernel32.dll.
Code
Imports System
Imports System.Runtime.InteropServices
Imports System.Security
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
using System;
using System.Runtime.InteropServices;
using System.Security;
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
Description
Les méthodes P/Invoke qui ne peuvent pas être appelées sans risque et qui peuvent provoquer des effets secondaires doivent être placées dans une classe nommée UnsafeNativeMethods. Ces méthodes doivent être vérifiées rigoureusement pour s'assurer qu'elles ne sont pas exposées involontairement à l'utilisateur. La règle CA2118 : Révision de l'utilisation de SuppressUnmanagedCodeSecurityAttribute peut y contribuer. Les méthodes doivent également avoir une autre autorisation demandée à la place du UnmanagedCode lorsqu'elles les utilisent.
L'exemple suivant montre une méthode Cursor.Hide qui encapsule la fonction ShowCursor à partir d'user32.dll.
Code
Imports System
Imports System.Runtime.InteropServices
Imports System.Security
Imports System.Security.Permissions
Public NotInheritable Class Cursor
Private Sub New()
End Sub
' Callers do not require Unmanaged permission, however,
' they do require UIPermission.AllWindows
Public Shared Sub Hide()
' Need to demand an appropriate permission
' in place of UnmanagedCode permission as
' ShowCursor is not considered a safe method
Dim permission As New UIPermission(UIPermissionWindow.AllWindows)
permission.Demand()
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
using System;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Permissions;
public static class Cursor
{
// Callers do not require UnmanagedCode permission, however,
// they do require UIPermissionWindow.AllWindows
public static void Hide()
{
// Need to demand an appropriate permission
// in place of UnmanagedCode permission as
// ShowCursor is not considered a safe method
new UIPermission(UIPermissionWindow.AllWindows).Demand();
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);
}