CA1060: Spostare i P/Invoke nella classe NativeMethods
Proprietà | valore |
---|---|
ID regola | CA1060 |
Title | Spostare P/Invoke nella classe NativeMethods |
Categoria | Progettazione |
La correzione causa un'interruzione o meno | Interruzione |
Abilitato per impostazione predefinita in .NET 9 | 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.
SafeNativeMethods : questa classe elimina le procedure 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 SafeNativeMethods 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, l'esempio SafeNativeMethods e l'esempio 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 SafeNativeMethods 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 safeNativeMethods
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 SafeNativeMethods. Non è necessario prestare molta attenzione a dove vengono chiamati.
Nell'esempio seguente viene illustrata 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);
}