题
我有一个Visual Studio 2008 C#.NET 3.5应用程序,该应用程序调用接受文件句柄作为参数的本机方法。最初,我只是在使用fileStream.safefilehandle.dangeregethandle()获取文件句柄。但是,打开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);
}
谢谢,保罗
解决方案
您在这里做几件事。
在您的安全代码执行时,在最后块中执行代码,以防止ThreadAbortExceptions。
在尝试/最终技巧之前,您调用PrepareContain Regions,基本上什么也没做,除了检查是否存在足够的线程堆栈空间,以确保至少可以调用一些方法调用,以免通过stackoverflowException措手不及安全的代码。
因此,是的,您的代码看起来很安全。在官员中 Docu 关于CERS,有人指出,CLR确实识别此尝试/最终阻止并采取其他措施。据我所知,除了在CER代码运行后也延迟了OutofMemoryExceptions,没有什么区别。
为了确保您的代码满足您的期望,您应该为这些事情创建测试。
- 堆栈耗尽
- 记不清
- 线程
编写可靠的代码真的很难,即使大多数BCL类也不是 硬化 反对乔·达菲(Joe Duffy)解释的事情。即使您的代码未失败,BCL代码也可以。直到BCL代码的主要部分可以以明确的方式应对这些极端条件之前,您将不会获得太大的收益。
你的,阿洛伊斯·克劳斯
其他提示
处理它的真实安全方法是通过Safehandle代替INTPTR参考 - P/Invoke层是安全的,并且会自动为您提供此工作。唯一的例外是,当您调用本机API关闭手柄时,由于您在使用它时安全hand被处置了。
例如:
[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;
}
}
但是,如果安全实现作为结构中的参数传递,或者基础手柄不仅仅是INTPTR,则可能是不可能的。例如,Win32 SSPI API使用两个INTPTR的手柄。要处理这种情况,您必须手动执行CER。
您的CER使用不正确。 DangerousAddRef
仍然会失败。以下是微软在其.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()
是否有机会泄漏内存或中止线程?