Question

J'ai un Visual Studio 2008 C # .NET 3.5 que l'application P / Invoque une méthode native qui accepte un descripteur de fichier en tant que paramètre. A l'origine, je voulais simplement en utilisant FileStream.SafeFileHandle.DangerousGetHandle () pour obtenir le handle de fichier. Mais, après la mise FX COP, je suis un avertissement CA2001 à ce sujet . Ainsi, après un peu de recherche que j'ai découvert « Constrained exécuter des régions ». Ce qui est nouveau pour moi et je l'ai pas vu beaucoup d'informations à ce sujet. J'espérais que quelqu'un plus expérimenté pourrait jeter un oeil et vérifier que je l'ai fait correctement.

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

Merci, PaulH

Était-ce utile?

La solution

Vous faites plusieurs choses ici.

  1. exécuter du code dans le bloc finally pour éviter tout ThreadAbortExceptions votre code de sécurité est en cours d'exécution.

  2. Avant le try / finally astuce que vous appelez PrepareConstrainedRegions qui ne fondamentalement rien, sauf pour vérifier que suffisamment d'espace de pile de fil est présent pour être sûr qu'au moins certains appels peuvent être faits si votre code sécurité ne sont pas pris au dépourvu par un StackOverflowException.

Alors oui vous regarde de code aussi sûr qu'il peut obtenir. Dans le fonctionnaire docu sur URCE il est indiqué que le CLR ne reconnaît cette try / finally blocs comme bien et prend des mesures supplémentaires. D'après ce que je l'ai vu il n'y a pas beaucoup de différence, sauf que OutOfMemoryExceptions sont également retardés après votre code CER a été exécuté.

Pour être vraiment sûr que votre code répond à vos attentes, vous devez créer des tests pour ces choses.

  • Stack Épuisement
  • Mémoire insuffisante
  • Thread.Abort

L'écriture du code fiable est vraiment difficile et même la plupart des classes de la BCL ne sont pas endurci contre des choses telles que Joe Duffy explique. Même si votre code ne manque pas le code de BCL. Vous ne serez pas beaucoup d'avantages ajoutés jusqu'à ce qu'une grande partie du code de BCL peut faire face à ces conditions extrêmes d'une manière bien définie.

Bien à vous, Alois Kraus

Autres conseils

La façon sécuritaire truely manipuler est de passer la SafeHandle à la place de la référence IntPtr - la couche P / Invoke est SafeHandle-Aware et fera ce travail pour vous automatiquement. La seule exception à cette règle est lorsque vous appelez l'API native de fermer votre poignée, puisque le SafeHandle devient disposé pendant que vous utilisez.

Par exemple:

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

Cependant, cela peut ne pas être possible si la mise en œuvre SafeHandle est passée comme paramètre dans une struct, ou si la poignée sous-jacente est plus un IntPtr. Par exemple, l'api SSPI Win32 utilise des poignées qui sont deux IntPtrs. Pour faire face à cette situation, vous devez faire la CER manuellement.

Votre utilisation du CER est incorrect. DangerousAddRef peut encore échouer. Ce qui suit est le modèle utilisé par Microsoft dans la source .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;
    }
}

Vous pouvez voir ce modèle en vigueur dans la Microsoft Reference Source- Voir _ SafeNetHandle.cs , ligne 2071.

Je ne vois pas comment vous pourriez avoir des problèmes du tout, à moins que vous générez des exceptions à l'intérieur du bloc try.

  • est le code dans la section finally atomique?
  • Est-ce NativeMethods.Foo() avoir une chance de fuite de mémoire ou un fil abandon?
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top