Pregunta

Tengo una aplicación Visual Studio 2008 C# .NET 3.5 que P/invoca un método nativo que acepta un mango de archivo como parámetro. Originalmente, solo estaba usando FileStream.SafefileHandle.Dangerousgethandle () para obtener el mango del archivo. Pero, después de encender FX Cop, obtuve un CA2001 Advertencia sobre eso. Entonces, después de una pequeña investigación, descubrí "regiones de ejecución restringidas". Esto es nuevo para mí y no he visto mucha información al respecto. Esperaba que alguien con más experiencia pudiera echar un vistazo y verificar que lo haya hecho correctamente.

class MyClass
{
    public static bool Write(string filename)
    {
        using (var fs = new System.IO.FileStream(filename, 
            System.IO.FileMode.Create, 
            System.IO.FileAccess.Write, 
            System.IO.FileShare.None))
        {
            bool got_handle;
            bool result;

            System.Runtime.CompilerServices.RuntimeHelpers.PrepareConstrainedRegions();
            try { }
            finally
            {
                fs.SafeFileHandle.DangerousAddRef(ref got_handle);
                result = NativeMethods.Foo(fs.SafeFileHandle.DangerousGetHandle());
                if (got_handle)
                    fs.SafeFileHandle.DangerousRelease();   
            }

            return result;
        }
    }
}

internal sealed class NativeMethods
{
    [DllImport("mylib.dll",
        EntryPoint = "Foo",
        CallingConvention = CallingConvention.StdCall,
        CharSet = CharSet.Unicode,
        ExactSpelling = true, 
        SetLastError = true)]
    public static extern bool Foo(IntPtr hFile);
}

Gracias Paulh

¿Fue útil?

Solución

Estás haciendo varias cosas aquí.

  1. Ejecute el código en el bloque Finalmente para evitar threadabortexcepciones mientras se ejecuta su código seguro.

  2. Antes del tráfico de intento/finalmente llame a PrepareConstrainEgnineRegions, que básicamente no hace nada excepto para verificar que haya suficiente espacio de pila de subprocesos para asegurarse de que al menos algunas llamadas de método se puedan hacer para que su código seguro no se ponga desprevenido por un stackoverflowexception.

Entonces, sí, su código se ve tan seguro como puede ser. En el funcionario docu Acerca de CERS se afirma que el CLR también reconoce este intento/finalmente bloqueos y toma medidas adicionales. Por lo que he visto, no hay mucha diferencia, excepto que OutOfMemoryExcepciones también se retrasa después de que su código CER se ejecute.

Para estar realmente seguro de que su código cumple con sus expectativas, debe crear pruebas para estas cosas.

  • Agotamiento de la pila
  • Sin memoria
  • Hilo

Escribir código confiable es realmente difícil e incluso la mayoría de las clases de BCL no son curtido Contra cosas como Joe Duffy explica. Incluso si su código no falla, el código BCL puede. No obtendrá muchos beneficios adicionales hasta que una parte importante del código BCL pueda hacer frente a estas condiciones extremas de una manera bien definida.

Tuyo, Alois Kraus

Otros consejos

La forma verdaderamente segura de manejarlo es pasar el SafeHandle en lugar de la referencia INTPTR: la capa P/Invoke es SafeHandle y hará que esto funcione automáticamente para usted. La única excepción a esto es cuando llamas a la API nativa para cerrar tu mango, ya que SafeHandle se elimina mientras lo usas.

Por ejemplo:

[DllImport( "qwave.dll", CallingConvention = CallingConvention.Winapi, SetLastError = true )]
internal static extern bool QOSCreateHandle( ref QosVersion version, out QosSafeHandle qosHandle );

[DllImport( "qwave.dll", CallingConvention = CallingConvention.Winapi, SetLastError = true )]
[ReliabilityContract( Consistency.WillNotCorruptState, Cer.Success )]
internal static extern bool QOSCloseHandle( IntPtr qosHandle );

[DllImport( "qwave.dll", CallingConvention = CallingConvention.Winapi, SetLastError = true )]
internal static extern bool QOSAddSocketToFlow(
    QosSafeHandle qosHandle,
    IntPtr socket,
    byte[] destAddr,
    QosTrafficType trafficType,
    QosFlowFlags flags,
    ref uint flowId
);


/// <summary>
/// Safely stores a handle to the QWave QoS win32 API and ensures the handle is properly 
/// closed when all references to the handle have been garbage collected.
/// </summary>
public class QosSafeHandle : SafeHandle
{
    /// <summary>
    /// Initializes a new instance of the QosSafeHandle class.
    /// </summary>
    public QosSafeHandle() :
        base( IntPtr.Zero, true )
    {
    }

    /// <summary>
    /// Whether or not the handle is invalid.
    /// </summary>
    public override bool IsInvalid
    {
        get { return this.handle == IntPtr.Zero; }
    }

    /// <summary>
    /// Releases the Qos API instance handle.
    /// </summary>
    /// <returns></returns>
    protected override bool ReleaseHandle()
    {
        QosNativeMethods.QOSCloseHandle( this.handle );
        return true;
    }
}

Sin embargo, esto puede no ser posible si la implementación de SafeHandle se está pasando como un parámetro en una estructura, o si el mango subyacente es más que un INTPTR. Por ejemplo, la API SSPI Win32 utiliza manijas que son dos INTPTR. Para lidiar con esa situación, debes hacer el CER manualmente.

Su uso de CER es incorrecto. DangerousAddRef todavía puede fallar. El siguiente es el patrón utilizado por Microsoft en su fuente .NET:

public static bool Write( string filename )
{
    using( var fs = new System.IO.FileStream( filename,
        System.IO.FileMode.Create,
        System.IO.FileAccess.Write,
        System.IO.FileShare.None ) )
    {
        bool got_handle;
        bool result;

        // The CER is here to ensure that reference counting on fs.SafeFileHandle is never
        // corrupted. 
        RuntimeHelpers.PrepareConstrainedRegions();
        try
        {
            fs.SafeFileHandle.DangerousAddRef( ref got_handle );
        }
        catch( Exception e )
        {
            if( got_handle )
            {
                fs.SafeFileHandle.DangerousRelease();
            }

            got_handle = false;

            throw;
        }
        finally
        {
            if( got_handle )
            {
                result = NativeMethods.Foo( fs.SafeFileHandle.DangerousGetHandle() );

                fs.SafeFileHandle.DangerousRelease();
            }
        }

        return result;
    }
}

Puede ver este patrón vigente en la fuente de referencia de Microsoft, ver _Safenethandle.cs, línea 2071.

No veo cómo podrías tener ningún problema, a menos que genere excepciones dentro del try bloquear.

  • Es el código dentro del finally sección atómica?
  • Lo hace NativeMethods.Foo() ¿Tiene alguna posibilidad de filtrar memoria o abortar un hilo?
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top