Использование ограниченных областей выполнения

StackOverflow https://stackoverflow.com/questions/5330799

  •  26-10-2019
  •  | 
  •  

Вопрос

У меня есть приложение Visual Studio 2008 C# .net 3.5, которое P/вызывает нативный метод, который принимает дескриптор файла в качестве параметра. Первоначально я просто использовал fileStream.SafefileHandle.DangErygethandle (), чтобы получить ручку файла. Но, включив полицейский FX, я получил CA2001 Предупреждение об этом. Итак, после небольшого исследования я обнаружил «ограниченные регионы». Это новое для меня, и я не видел много информации об этом. Я надеялся, что кто -то более опытный сможет взглянуть и проверить, что я сделал это правильно.

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

Спасибо, Полх

Это было полезно?

Решение

Вы делаете здесь несколько вещей.

  1. Выполните код в блоке, наконец, чтобы предотвратить ThreadAbortExceptions во время выполнения вашего безопасного кода.

  2. Прежде чем попытка/, наконец, трюк, который вы называете PrepareCondessedRegions, которые в основном не делают ничего, кроме того, чтобы проверить, что достаточно места стека потоков, чтобы быть уверенным, что, по крайней мере, некоторые вызовы метода могут быть сделаны, чтобы ваш безопасный код не был заставлен врасплох в связи с помощью StackOverflowException.

Так что да, код выглядит настолько безопасным, насколько это возможно. На чиновнике док О CERS заявлено, что CLR действительно распознает эту блоки TRY/, наконец, и принимает дополнительные меры. Из того, что я видел, нет большой разницы, за исключением того, что OutofmemoryExceptions также задерживаются после того, как ваш код CER будет выполнен.

Чтобы быть уверенным, что ваш код соответствует вашим ожиданиям, вы должны создавать тесты для этих вещей.

  • Стека истощение
  • Недостаточно памяти
  • Thread.Abort

Написание надежного кода действительно сложно, и даже большинство классов BCL не закален против таких вещей, как объясняет Джо Даффи. Даже если ваш код не снят сбой, код BCL может. Вы не получите много дополнительной выгоды, пока большая часть кода BCL не сможет справиться с этими экстремальными условиями четко определенным образом.

Твой, Алоис Краус

Другие советы

Действительно безопасный способ справиться с ним состоит в том, чтобы пройти SafeHandle вместо ссылки на INTPTR - слой P/Invoke является безопасным, а также будет автоматически заработать для вас работу. Единственное исключение из этого - когда вы называете родной API, чтобы закрыть ручку, так как SafeHandle утилизируется во время его использования.

Например:

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

Тем не менее, это может быть невозможно, если реализация SafeHandle передается как параметр в структуре, или если базовая ручка является более чем INTPTR. Например, API WIN32 SSPI использует ручки, которые являются двумя INTPTR. Чтобы справиться с этой ситуацией, вы должны сделать CER вручную.

Ваше использование CER неверно. DangerousAddRef все еще может потерпеть неудачу. Ниже приведен шаблон, используемый Microsoft в их источнике .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;
    }
}

Вы можете увидеть этот шаблон, действующий в справочном источнике Microsoft- см. _Safenethandle.cs, строка 2071.

Я не вижу, как у вас могут возникнуть какие -либо проблемы, если только вы не создаете исключения внутри try блокировать.

  • Код внутри finally Раздел «Атомный»?
  • Делает NativeMethods.Foo() У вас есть шанс утечь память или прервать ветку?
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top