Condividi tramite


Spostare i P/Invoke nella classe dei metodi nativi

Aggiornamento: novembre 2007

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 quelli contrassegnati con l'attributo System.Runtime.InteropServices.DllImportAttribute, o i metodi 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 protezione 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 protezione per assicurare 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).

Correzione di 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.

Utilizzare questo attributo con molta cautela perché, 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

Descrizione

Poiché la classe NativeMethods non deve essere contrassegnata con SuppressUnmanagedCodeSecurityAttribute, i P/Invoke contenuti richiederanno l'autorizzazione UnmanagedCode. Perché la maggior parte delle applicazioni viene eseguito 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

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

Descrizione

I metodi P/Invoke che per alcuni utenti è sconsigliabile chiamare e che potrebbero provocare effetti collaterali devono essere memorizzati in una classe denominata UnsafeNativeMethods. Questi metodi devono essere controllati rigorosamente per assicurarsi che non vengano esposti involontariamente all'utente. La regola Rivedere l'utilizzo 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);   
}

Vedere anche

Altre risorse

Avvisi di progettazione