Compartir a través de


CA1060: Mueva P/Invokes a la clase NativeMethods

Nombre de tipo

MovePInvokesToNativeMethodsClass

Identificador de comprobación

CA1060

Categoría

Microsoft.Design

Cambio problemático

Causa

Un método utiliza los servicios de invocación de plataforma para tener acceso al código no administrado y no es un miembro de una de las clases NativeMethods.

Descripción de la regla

Los métodos de invocación de plataforma, como los marcados mediante el atributo System.Runtime.InteropServices.DllImportAttribute (o los métodos definidos utilizando la palabra clave Declare en Visual Basic) acceden al código no administrado. Estos métodos deberían estar en una de las clases siguientes:

  • NativeMethods: esta clase no suprime los recorridos de pilas para el permiso de código no administrado. (System.Security.SuppressUnmanagedCodeSecurityAttribute no se debe aplicar a esta clase.) Esta clase es para los métodos que se pueden utilizar en cualquier parte porque se realizará un recorrido de pila.

  • SafeNativeMethods: esta clase suprime los recorridos de pila para el permiso de código no administrado. (System.Security.SuppressUnmanagedCodeSecurityAttribute se aplica a esta clase.) Esta clase es para los métodos que son seguros para llamar. Los llamadores de estos métodos no requieren que se realice una revisión completa de seguridad para garantizar que su uso es seguro puesto que los métodos son inofensivos para los llamadores.

  • UnsafeNativeMethods: esta clase suprime los recorridos de pila para el permiso de código no administrado. (System.Security.SuppressUnmanagedCodeSecurityAttribute se aplica a esta clase.) Esta clase es para métodos que son potencialmente peligrosos. Cualquier llamador de estos métodos debe realizar una revisión de seguridad completa para garantizar que su uso es seguro puesto que no se realizará ningún recorrido de pila.

Estas clases se declaran como internal (Friend en Visual Basic) y declaran un constructor privado para evitar que se creen nuevas instancias. Los métodos de estas clases deberían ser static e internal (Shared y Friend en Visual Basic).

Cómo corregir infracciones

Para corregir una infracción a esta regla, mueva el método a la clase NativeMethods adecuada. Para la mayoría de las aplicaciones, basta con mover los elementos P/Invoke a una nueva clase denominada NativeMethods.

Sin embargo, si va a desarrollar bibliotecas para usarlas en otras aplicaciones, debe considerar la posibilidad de definir otras dos clases denominadas SafeNativeMethods y UnsafeNativeMethods. Estas clases se parecen a la clase NativeMethods; sin embargo, se marcan mediante un atributo especial denominado SuppressUnmanagedCodeSecurityAttribute. Cuando se aplica este atributo, el motor en tiempo de ejecución no realiza un recorrido de pila completo para asegurarse de que todos los llamadores tienen el permiso UnmanagedCode. El motor en tiempo de ejecución comprueba normalmente este permiso durante el inicio. Dado que no se realiza la comprobación, se puede mejorar considerablemente el rendimiento de las llamadas a estos métodos no controlados, además de permitir que código con permisos limitados llame a estos métodos.

Sin embargo, debería utilizar este atributo con gran cuidado. Puede tener serias implicaciones de seguridad si se implementa incorrectamente.

Para obtener información sobre cómo implementar los métodos, vea el ejemplo de NativeMethods, el ejemplo de SafeNativeMethods y el ejemplo de UnsafeNativeMethods.

Cuándo suprimir advertencias

No suprima las advertencias de esta regla.

Ejemplo

El siguiente ejemplo declara un método que infringe esta regla. Para corregir la infracción, el elemento P/Invoke de RemoveDirectory se debe mover a una clase adecuada que esté diseñada para almacenar únicamente elementos 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);
    }
}

Ejemplo de NativeMethods

Descripción

Dado que la clase NativeMethods no se debe marcar mediante SuppressUnmanagedCodeSecurityAttribute, los elementos P/Invoke incluidos dentro de ella requerirán el permiso UnmanagedCode. Como la mayoría de las aplicaciones se ejecutan desde el equipo local y conjuntamente con plena confianza, esto no suele suponer un problema. Sin embargo, si va a desarrollar bibliotecas reutilizables, debe considerar la posibilidad de definir una clase SafeNativeMethods o UnsafeNativeMethods.

En el ejemplo siguiente se muestra un método Interaction.Beep que contiene la función MessageBeep de user32.dll. El elemento P/Invoke de MessageBeep se coloca en la clase NativeMethods.

Código

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

Ejemplo de SafeNativeMethods

Descripción

Los métodos de P/Invoke que pueden exponerse a cualquier aplicación sin peligro y que no tienen efectos secundarios se deben colocar en una clase denominada SafeNativeMethods. De esta forma, no tendrá que requerir permisos ni prestar demasiada atención al lugar del que se llaman.

En el ejemplo siguiente se muestra una propiedad Environment.TickCount que contiene la función GetTickCount de kernel32.dll.

Código

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

Ejemplo de UnsafeNativeMethods

Descripción

Los métodos P/Invoke a los que no se pueden llamar sin riesgo y que podrían producir efectos secundarios se deberían colocar en una clase que se denomina UnsafeNativeMethods. Estos métodos deben comprobarse exhaustivamente para garantizar que no se exponen al usuario involuntariamente. La regla CA2118: Revisar el uso de SuppressUnmanagedCodeSecurityAttribute puede servir de ayuda para tal fin. Asimismo, los métodos deben tener otro permiso que se solicita en lugar de UnmanagedCode cuando se utilizan.

En el ejemplo siguiente se muestra un método Cursor.Hide que contiene la función ShowCursor de user32.dll.

Código

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

Vea también

Otros recursos

Diseñar advertencias