Mover elementos P/Invoke a la clase NativeMethods
Actualización: noviembre 2007
Nombre de tipo |
MovePInvokesToNativeMethodsClass |
Identificador de comprobación |
CA1060 |
Categoría |
Microsoft.Design |
Cambio problemático |
Sí |
Motivo
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 aquellos marcados con el atributo System.Runtime.InteropServices.DllImportAttribute o los métodos definidos utilizando la palabra clave Declare en Visual Basic, tienen acceso 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, pero están marcadas con 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, debe utilizar este atributo con precaución, ya que una implementación incorrecta puede tener graves consecuencias para la seguridad.
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
Description
Dado que la clase NativeMethods no se debe marcar con 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 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 dentro de 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
Description
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
Description
Los métodos de P/Invoke a los que no es seguro llamar y que pueden causar efectos secundarios se deben colocar en una clase denominada UnsafeNativeMethods. Estos métodos deben comprobarse exhaustivamente para garantizar que no se exponen al usuario involuntariamente. La regla Revisar el uso de SuppressUnmanagedCodeSecurityAttribute puede servir de ayuda para tal fin. Asimismo, los métodos deben haber solicitado otro permiso 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);
}