CA1060: Spostare i P/Invoke nella classe NativeMethods
TypeName |
MovePInvokesToNativeMethodsClass |
CheckId |
CA1060 |
Category |
Microsoft.Design |
Breaking Change |
Breaking |
Causa
Un metodo utilizza Platform Invocation Services per accedere a codice non gestito e non è un membro di una delle classi NativeMethods.
Descrizione della regola
I metodi di chiamata al sistema operativo, ad esempio i metodi contrassegnati con l'attributo System.Runtime.InteropServices.DllImportAttribute o quelli definiti mediante la parola chiave Declare in Visual Basic accedono a codice non gestito. Questi metodi devono essere contenuti in una delle classi riportate di seguito:
NativeMethods: questa classe non elimina i percorsi stack per l'autorizzazione del codice non gestito. System.Security.SuppressUnmanagedCodeSecurityAttribute non deve essere applicato a questa classe). Questa classe è destinata ai metodi che possono essere utilizzati ovunque poiché verrà eseguito un percorso stack.
SafeNativeMethods: questa classe elimina i percorsi stack per l'autorizzazione del codice non gestito. System.Security.SuppressUnmanagedCodeSecurityAttribute viene applicato a questa classe). Questa classe è destinata ai metodi che chiunque può chiamare in modo sicuro. Ai chiamanti di questi metodi non viene richiesto di eseguire una revisione completa della sicurezza per assicurare che l'utilizzo sia sicuro poiché i metodi sono innocui per qualsiasi chiamante.
UnsafeNativeMethods: questa classe elimina i percorsi stack per l'autorizzazione del codice non gestito. System.Security.SuppressUnmanagedCodeSecurityAttribute viene applicato a questa classe). Questa classe è destinata ai metodi potenzialmente pericolosi. Qualsiasi chiamante di questi metodi deve eseguire una revisione completa della sicurezza per garantire che l'utilizzo sia sicuro poiché non verrà eseguito alcun percorso stack.
Queste classi sono dichiarate come internal (Friend in Visual Basic) e dichiarano un costruttore privato per impedire la creazione di nuove istanze. I metodi di 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 è sufficiente spostare i P/Invoke in una nuova classe denominata NativeMethods.
Se tuttavia si stanno sviluppando librerie da utilizzare nelle altre applicazioni, è opportuno definire due altre classi chiamate SafeNativeMethods e UnsafeNativeMethods. Queste classi assomigliano alla classe NativeMethods, tuttavia sono contrassegnate con un attributo speciale denominato SuppressUnmanagedCodeSecurityAttribute. Quando questo attributo è applicato, il runtime non esegue un percorso stack completo per assicurarsi che tutti i chiamanti dispongano dell'autorizzazione UnmanagedCode. Il runtime verifica solitamente questa autorizzazione all'avvio. La mancata esecuzione di questo controllo migliora notevolmente le prestazioni di tutte le chiamate a questi metodi non gestiti, i quali possono venire chiamati anche dal codice con autorizzazioni limitate.
Tuttavia, è necessario utilizzare questo attributo con estrema attenzione. Se erroneamente implementato, può compromettere gravemente la sicurezza.
Per informazioni sull'implementazione dei metodi, vedere gli esempi NativeMethods, SafeNativeMethods e UnsafeNativeMethods.
Esclusione di avvisi
Non escludere un avviso da questa regola.
Esempio
Nell'esempio riportato di seguito viene dichiarato un metodo che viola questa regola. Per correggere la violazione, è necessario spostare i P/Invoke RemoveDirectory in una classe appropriata progettata per contenere solo 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);
}
}
Esempio NativeMethods
Oggetto di descrizione
Poiché la classe NativeMethods non deve essere contrassegnata con SuppressUnmanagedCodeSecurityAttribute, i P/Invoke contenuti richiederanno l'autorizzazione UnmanagedCode. Poiché la maggior parte delle applicazioni viene eseguita dal computer locale e con attendibilità, questo non è generalmente un problema. Se tuttavia si stanno sviluppando librerie riutilizzabili, è opportuno definire una classe SafeNativeMethods o UnsafeNativeMethods.
Nell'esempio seguente viene illustrato un metodo Interaction.Beep che esegue il wrapping della funzione MessageBeep da user32.dll. Il P/Invoke MessageBeep viene inserito nella classe NativeMethods.
Codice
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);
}
Esempio SafeNativeMethods
Oggetto di descrizione
I metodi di P/Invoke che possono essere esposti a una qualsiasi applicazione senza provocare effetti collaterali devono essere memorizzati in una classe denominata SafeNativeMethods. Non è necessario richiedere autorizzazioni né conoscere da dove vengono chiamati.
Nell'esempio seguente viene illustrata una proprietà Environment.TickCount che esegue il wrapping della funzione GetTickCount da kernel32.dll.
Codice
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();
}
Esempio UnsafeNativeMethods
Oggetto di descrizione
I metodi P/Invoke che è sconsigliabile chiamare e che potrebbero provocare effetti collaterali devono essere inseriti in una classe denominata UnsafeNativeMethods. Questi metodi devono essere controllati rigorosamente per assicurarsi che non vengano esposti involontariamente all'utente. La regola CA2118: Verificare la sintassi di SuppressUnmanagedCodeSecurityAttribute può essere di aiuto in questo caso. In alternativa, può essere opportuno richiedere a questi metodi un'altra autorizzazione anziché UnmanagedCode durante l'utilizzo.
Nell'esempio seguente viene illustrato un metodo Cursor.Hide che esegue il wrapping della funzione ShowCursor da user32.dll.
Codice
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);
}