使用约束执行区域

我有一个Visual Studio 2008 C#.NET 3.5应用程序P / Invokes一个接受文件句柄作为参数的本机方法。 最初,我只是使用FileStream.SafeFileHandle.DangerousGetHandle()来获取文件句柄。 但是,在启用FX COP之后,我收到了CA2001的警告。 因此,经过一番研究后,我发现了“约束执行区域”。 这对我来说是新的,我还没有看到很多关于它的信息。 我希望有经验的人可以看看并validation我是否已正确完成此操作。

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

谢谢,PaulH

你在这里做了几件事。

  1. 执行finally块中的代码,以防止执行安全代码时出现ThreadAbortExceptions。

  2. 在try / finally技巧之前,你调用PrepareConstrainedRegions,除了检查是否存在足够的线程堆栈空间以确保至少可以进行一些方法调用以确保您的安全代码不会被StackOverFlowException猝不及防。

所以是的,您的代码看起来尽可能安全。 在有关CERs的官方文件中 ,有人说CLR确实认识到这个try / finally块并采取了额外的措施。 从我所看到的情况来看,除了在CER代码运行后OutOfMemoryExceptions也被延迟之外没有太大区别。

为了确保您的代码符合您的期望,您应该为这些事情创建测试。

  • 堆栈耗尽
  • 内存不足
  • Thread.Abort的

编写可靠的代码真的很难,甚至大多数BCL类都没有像Joe Duffy解释的那样强硬 。 即使您的代码没有失败,BCL代码也可以。 在BCL代码的主要部分能够以明确定义的方式处理这些极端条件之前,您不会获得太多额外的好处。

你的,Alois Kraus

处理它的真正安全的方法是传递SafeHandle代替IntPtr引用 – P / Invoke层是SafeHandle感知的,并且将自动为您工作。 唯一的例外是当您调用本机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 ); ///  /// 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. ///  public class QosSafeHandle : SafeHandle { ///  /// Initializes a new instance of the QosSafeHandle class. ///  public QosSafeHandle() : base( IntPtr.Zero, true ) { } ///  /// Whether or not the handle is invalid. ///  public override bool IsInvalid { get { return this.handle == IntPtr.Zero; } } ///  /// Releases the Qos API instance handle. ///  ///  protected override bool ReleaseHandle() { QosNativeMethods.QOSCloseHandle( this.handle ); return true; } } 

但是,如果将SafeHandle实现作为结构中的参数传递,或者底层句柄不仅仅是IntPtr,则可能无法实现。 例如,Win32 SSPI api使用两个IntPtrs的句柄。 要处理这种情况,您必须手动执行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 Reference Source中看到此模式生效 – 请参阅_SafeNetHandle.cs ,第2071行。

除非你在try块中生成exception,否则我看不出你有什么问题。

  • finally段内的代码是primefaces的吗?
  • NativeMethods.Foo()是否有泄漏内存或中止线程的可能性?