問題:
アンマネージ コードからCLRに入るスレッドの未処理の例外は、「通常の」未処理の例外 CLR 処理をトリガーしません。
以下のコードではCSSimpleObject.GetstringLength()
、C++ から
- "1" は、呼び出し元のスレッド (CLR で作成されていないスレッド) で例外をスローします。
- "2" は、new Thread() (CLR で作成されたスレッド) で例外をスローします。
「1」の場合
- CurrentDomain_UnhandledException() が呼び出されることはありません。
- アプリケーション ドメインとプロセスはロードされたまま実行され、FAILED のみが表示されます。
「2」の場合(想定される動作)
- CurrentDomain_UnhandledException() が呼び出されます。
- プロセスは強制終了されます。
質問:
「通常の」動作を得るために何をしなければなりませんか?
サンプルコード:
以下のコードは、「すべての相互運用と融合のサンプル」の Visual Studio 2010 の「 CppHostCLR」コード サンプルに基づいています。
ランタイムホスト (C++):
PCWSTR pszStaticMethodName = L"GetStringLength";
PCWSTR pszStringArg = L"1";
//PCWSTR pszStringArg = L"2";
hr = pClrRuntimeHost->ExecuteInDefaultAppDomain(pszAssemblyPath,
pszClassName, pszStaticMethodName, pszStringArg, &dwLengthRet);
if (FAILED(hr))
{
wprintf(L"Failed to call GetStringLength w/hr 0x%08lx\n", hr);
goto Cleanup;
}
マネージド コード (C#):
public class CSSimpleObject
{
public CSSimpleObject()
{
}
//------8<----------
public static int GetStringLength(string str)
{
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
switch (str)
{
case "1":
throw new Exception("exception in non-CLR-created thread");
case "2":
Thread thread = new Thread(new ThreadStart(WorkThreadFunction));
thread.Start();
break;
}
return str.Length;
}
static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
Console.WriteLine("CurrentDomain_UnhandledException:" + e.ToString());
Console.ReadKey();
}
public static void WorkThreadFunction()
{
throw new Exception(""exception in CLR-created thread"");
}
これまでの研究:
MSDN は当初、非 CLR で作成されたスレッドで未処理の例外が多かれ少なかれ「自然に」動作する必要があることを暗示しています。「マネージ スレッドの例外」を参照してください。
共通言語ランタイムでは、スレッド内の未処理の例外のほとんどが自然に処理されます。ほとんどの場合、これは未処理の例外が原因でアプリケーションが終了することを意味します。」
「ほとんど」とは、CLR で作成されたスレッドの内部、スレッドの中止、およびアプリケーション ドメインのアンロード例外が異なる方法で処理されることを意味します。非 CLR スレッド内
「彼らは正常に進行し、アプリケーションは終了します。」
さらに調査した結果、「CLR で処理されない例外処理」にたどり着き、次のことがわかりました。
「例外が処理されなかった場合...マネージメソッドでは、例外はCLRを終了しますが、ネイティブSEH例外としてスタックを伝播し続けます(マネージ例外はネイティブSEH例外として表されます)... OS未処理例外フィルター (UEF) メカニズムによって、CLR の未処理の例外処理が常にトリガーされるとは限りません。通常の状況では、これは期待どおりに機能し、CLR の未処理の例外処理がトリガーされます。ただし、特定のインスタンスでは、これが発生しない場合があります。」
上記のコードの何が問題なのですか、または CLR の未処理の例外処理がトリガーされるように変更するにはどうすればよいですか?
更新 (2011-05-31):
私は古いバグ レポートを見つけました。
この問題を報告していただきありがとうございます。この動作は実際には、CLR 実行エンジンと CRT が UnhandledExceptionFilter をめぐって競合することによって引き起こされるバグです。CLR のアーキテクチャは、このシナリオをサポートするバージョン 4.0 で改訂されました。
更新 (2011-06-06):
これを正しく理解することがなぜ重要なのでしょうか?
- ホスティング環境を作成している場合、開発者は例外処理で一貫した動作を期待しています
- ネイティブ スレッドで「通常の CLR 例外処理」をトリガーする方法がない限り、常にマネージド スレッドに実行を転送する必要があることを意味します (たとえば、スレッド プールでのエンキュー)。
- 実行をネイティブ スレッドからマネージド スレッドに転送する小さなコードがまだあります。すべての例外をキャッチし、何らかの方法でその状況を別の方法で処理する必要があります。
注: CLR の動作を変更するSetActionOnFailure()
と、元の例外がマスクされてしまうという意味で、問題が悪化します (つまり、メモリ不足の代わりに、元のエラーがどこから来たのか手がかりがなくて、threadAborts が表示されます)。