質問
Visual Studio 2008 C#.NET 3.5アプリケーションは、パラメーターとしてファイルハンドルを受け入れるネイティブメソッドを呼び出すアプリケーションです。もともと、私はFileStream.SafileHandle.DangerousGethandle()を使用してファイルを取得していました。しかし、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を防止します。
Try/Trickの前に、StackOverFlowExceptionによって安全なコードが不意を突かれないように、少なくともいくつかのメソッド呼び出しが行われることを確認するために、十分なスレッドスタックスペースが存在することを確認する以外は、基本的には何もしないPrepereConstrainedRegionsに電話します。
はい、コードはそれが得られるほど安全に見えます。公式で docu CERについては、CLRがこの試行/最終的なブロックも認識し、追加の措置を講じることを認識していると述べています。私が見たものから、CERコードが実行された後にOutFmemoryExceptionsも遅延していることを除いて、大きな違いはありません。
あなたのコードがあなたの期待を満たしていることを本当に確実にするために、あなたはこれらのもののテストを作成する必要があります。
- スタックの疲労
- 記憶から
- thread.abort
信頼できるコードを書くことは本当に難しく、ほとんどのBCLクラスでさえもそうではありません 硬化 ジョー・ダフィーが説明するようなことに対して。コードが失敗しなくても、BCLコードはできます。 BCLコードの大部分がこれらの極端な条件によく定義された方法で対処できるようになるまで、あまり追加のメリットを得ることはできません。
あなた、アロイス・クラウス
他のヒント
それを処理するための本当に安全な方法は、INTPTRリファレンスの代わりにセーフハンドルを渡すことです - P/InvokeレイヤーはSafeHandle -Awareであり、これを自動的に作業させます。これの唯一の例外は、セーフハンドルが使用中に処分されるため、ハンドルを閉じるためにネイティブAPIを呼び出すときです。
例えば:
[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以上である場合、不可能です。たとえば、Win32 SSPI APIは、2つの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()
記憶を漏らしたり、スレッドを中止したりする可能性はありますか?