Domanda

Ho un Visual Studio 2008 C # .NET 3.5 applicazione che P / richiama un metodo nativo che accetta un handle di file come parametro. Originariamente, stavo usando FileStream.SafeFileHandle.DangerousGetHandle () per ottenere l'handle di file. Ma, dopo aver acceso COP FX, ho ricevuto una CA2001 avvertimento riguardo che . Così, dopo una piccola ricerca ho scoperto "Constrained eseguire regioni". Questo è nuovo per me e non ho visto un sacco di informazioni su di esso. Speravo che qualcuno più esperto potrebbe dare un'occhiata e verificare che ho fatto questo in modo corretto.

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

Grazie, Paulh

È stato utile?

Soluzione

Si sta facendo diverse cose qui.

  1. eseguire codice nel blocco finally per evitare ThreadAbortExceptions mentre il codice di sicurezza è in esecuzione.

  2. Prima che il try / finally trucco si chiama PrepareConstrainedRegions che non fa praticamente nulla, se non per verificare che lo spazio di stack filo sufficiente è presente per essere sicuri che almeno alcune chiamate di metodo possono essere effettuate in modo che il codice di sicurezza non è colto alla sprovvista da uno StackOverflowException.

Quindi, sì è sguardi di codice sicuro come si può ottenere. Nella Gazzetta docu sui CER vi si afferma che il CLR riconosce questo try / finally blocchi come bene e prende misure aggiuntive. Da quello che ho visto, non c'è molta differenza, tranne che OutOfMemoryExceptions sono in ritardo anche dopo che il codice CER ha funzionato.

Per essere veramente sicuri che il codice soddisfi le vostre aspettative si dovrebbe creare test per queste cose.

  • all'esaurimento dello stack
  • Out Of Memory
  • Thread.Abort

La scrittura di codice affidabile è davvero difficile e anche la maggior parte delle classi BCL non sono temprato contro le cose come spiega Joe Duffy. Anche se il codice non manca il codice BCL lattina. Non sarà possibile ottenere molto beneficio aggiunto fino a quando una parte importante del codice BCL può far fronte a queste condizioni estreme in un modo ben definito.

Distinti, Alois Kraus

Altri suggerimenti

Il modo veramente sicuro per gestire la cosa è di passare il SafeHandle al posto del riferimento IntPtr - il P / Invoke strato è SafeHandle-consapevole e farà questo lavoro per voi automaticamente. L'unica eccezione a questo è quando si sta chiamando l'API native di chiudere il manico, dal momento che la SafeHandle diventa disposta mentre lo si utilizza.

Per esempio:

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

Tuttavia, questo potrebbe non essere possibile se l'attuazione SafeHandle viene passato come parametro in una struttura, o se la maniglia sottostante è più di un IntPtr. Per esempio, l'API Win32 SSPI utilizza maniglie che sono due IntPtrs. Per far fronte a tale situazione, si deve fare il CER manualmente.

L'utilizzo CER non è corretto. DangerousAddRef può ancora fallire. Quanto segue è il modello utilizzato da Microsoft nella loro fonte .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;
    }
}

È possibile vedere questo modello a tutti gli effetti della Microsoft Reference Source-See _ SafeNetHandle.cs , linea 2071.

non vedo come si potrebbe avere problemi a tutti, a meno che non si genera eccezioni all'interno del blocco try.

  • Il codice all'interno della sezione finally atomica?
  • non NativeMethods.Foo() avere qualche possibilità di perdite di memoria o l'interruzione di un filo?
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top