マネージ コードによってマネージ例外がスローされ、キャッチされる可能性はありますが、コール スタックにネイティブ フレームが介在している場合はありますか?
これを行うのに問題があります。アプリは 32 ビットのネイティブ コードで、MSCLR 2.0 をホストします (ただし、ほとんどのコードは .NET 3.5 です)。
このスローが行われない限り、アプリケーションは正常に動作します。スローされたときに正確に何が起こるかは、アプリケーションが実行されているシステムによって異なります。
実際のアプリケーションは非常に複雑なので、少なくとも最初は説明のために簡単な概念コードを投稿します。ネイティブ プログラム ( と呼びますNative.exe
) は、 と呼ぶ単一のマネージ プログラムを実行しますManaged.exe
。Managed.exe
C# で記述された内のどこかに、次のものがあります。
class MyException : Exception {}
...
void OuterManaged()
{
MyObject.MyEvent += ( s, a ) =>
{
Console.WriteLine( "Throwing..." );
throw new MyException();
}
try
{
MyKernel.DoSomething();
Console.WriteLine( "Finished" );
} catch( MyException )
{
Console.WriteLine( "Caught" );
}
}
MyKernel
は、混合 C++/CLI アセンブリで定義されたマネージ クラスであり、これを と呼びますGlue.dll
。 MyObject
の別のクラスのインスタンスですGlue.dll
。関連するメソッドは次のようになります。
void DoSomething( void )
{
_pMyNativeKernel->DoSomething();
}
DoSomething
Native.exe
仮想的に呼び出されるC++ 関数です。簡単に言うと、最終的には、 raise を実行するマネージ メソッドにコールバックすることになりGlue.dll
ますMyEvent
。
MyEvent
が発生し、プログラムが 32 ビット Windows XP システムで実行されている場合、意図したとおりに動作し、コンソールに次のように表示されます。
Throwing...
Caught
Windows 7 64 ビット システムで実行すると、代わりに次のようになります。
Throwing...
Finished
基本的に、例外は何もないところに消えていきます。何も起こらなかったかのように、すべてが実行され続けます。(例外は、ウィンドウの閉じるボタンを押すことに対応するため、ボタンがクリックされていないかのように動作します。)
リモート デスクトップ経由で Windows Server 2012 システムを実行すると、次のようになります。
Throwing...
そして、アプリケーション全体がクラッシュし、「Native.exe が動作を停止しました」というダイアログが表示され、次のようになります。
Description:
Stopped working
Problem signature:
Problem Event Name: CLR20r3
Problem Signature 01: Native.exe
Problem Signature 02: 0.0.0.0
Problem Signature 03: 5267c484
Problem Signature 04: 0
Problem Signature 05: 1.0.0.0
Problem Signature 06: 5272e299
Problem Signature 07: 208
Problem Signature 08: f
Problem Signature 09: MyException
OS Version: 6.2.9200.2.0.0.144.8
Locale ID: 1033
try
これは、 /がなかったら期待していたものcatch
です。
その環境で VS2008SP デバッガーの下で実行すると、デバッガーは初回例外をキャッチし、それを続行すると未処理の例外としてキャッチします。
DoSomething
ネイティブは最終的に Win32GetMessage
を呼び出してから を呼び出しDispatchMessage
、ネイティブからマネージへのコールバックはウィンドウ プロシージャから呼び出されるコードで発生することに注意してください。そのウィンドウは Direct3D で描画されます。マネージ プログラムは、Native.exe
すべてのウィンドウ操作と描画操作に「カーネル」を使用し、単独で Windows にアクセスすることはありません。
介在する関数はNative.exe
どれも例外をまったくキャッチしません。Win32 API 関数に例外ハンドラがあるかどうかはわかりません。私はそうは思いませんが、システム間で動作がどのように矛盾していたかをおそらく説明できる場合があります。
これは、Server 2012 での実際のコール スタックを大まかに示したもので、繰り返しの項目が切り取られています。
Managed!MyGame.ReInitDisplay.AnonymousMethod(object s = {Engine.Display}, System.EventArgs a = {System.EventArgs}) C# // throw site
Glue.dll!CDisplayBridge::OnClosePressed() C++
[Native to Managed Transition]
Native.EXE!EngineKern::CGfxDisplay_a::HandleClosePress() C++
Native.EXE!EngineKern::CGfxDisplay::WindowProc(HWND__ * hwnd=0x000610ac, unsigned int uMsg=16, unsigned int wParam=0, long lParam=0) C++
user32.dll!74a477d8()
[Frames below may be incorrect and/or missing, no symbols loaded for user32.dll]
user32.dll!74a47c44()
ntdll.dll!773e2f02()
user32.dll!74a48fed()
uxtheme.dll!7422254d()
user32.dll!74a475e7() // DefWindowProc
Native.EXE!EngineKern::CGfxDisplay::WindowProc(HWND__ * hwnd=0x000610ac, unsigned int uMsg=274, unsigned int wParam=61536, long lParam=4261024) C++
user32.dll!74a48a66() // DispatchMessage
Native.EXE!EngineKern::CKernel::DoEvent() C++
[Managed to Native Transition]
Glue.dll!Engine::Kernel::DoEvent() C++ // DoSomething in the example
MyClassLib!MyClassLib.Game.MainLoop() C#
MyClassLib!MyClassLib.Game.Play() C#
Managed!MyGame.Play() C#
Managed!Program.Main(string[] args = {string[0]}) C#
mscorlib.dll!System.AppDomain.ExecuteAssemblyByName(string assemblyName, System.Security.Policy.Evidence assemblySecurity, string[] args)
mscorlib.dll!System.AppDomain.ExecuteAssemblyByName(string assemblyName)
Glue.dll!__Engine__::AppDomainManager::Main(int pEngineKern = 15760932) C++
[Native to Managed Transition]
Native.EXE!EngineGlue::IManagedEntryPoint::Main(long pEngineKern=15760932) C++ // calls in by COM interop
Native.EXE!CClrHost::Run() C++
Native.EXE!wWinMain(HINSTANCE__ * hInstance=0x00e40000, HINSTANCE__ * hPrevInstance=0x00000000, wchar_t * lpCmdLine=0x01462a80, int nCmdShow=5) C++
Native.EXE!__tmainCRTStartup() C
Native.EXE!wWinMainCRTStartup() C
kernel32.dll!74ca8543()
ntdll.dll!773fac3c()
このシステム全体は長い間正常に動作していますが、以前はマネージ/ネイティブ トランジションで例外をスローする必要はありませんでした。ネイティブ ホストがネイティブからマネージドへのコールバックを実行しているかどうかを気にすることなく、マネージド アプリケーション コードが自由に例外をスローおよびキャッチできるようにしたいと考えています。
このようなトランジションで例外をスローすることについてオンラインで見つけたすべての情報は、常にネイティブ例外をキャッチするか、その逆を管理することに関するものです。これはマネージド キャッチ マネージドですが、介在するネイティブ フレームが問題を複雑にしています。
したがって、一般的にこのような投げ方に関する私の質問は次のとおりです。
これは機能するはずですか?Windows XPでも動作しますが、これが明確に定義された動作なのか、単に運が良かったのかはわかりません。
機能する場合、すべてのシステムで機能しない理由として考えられるものは何ですか?
それが機能しない場合は、すべてのマネージド コールバックを拡張してマネージド例外をキャッチし、それらをネイティブ例外でラップし、それをネイティブ関数のマネージド ラッパーでキャッチして、元のマネージド例外をスローする必要があると思います。 . それはたくさんの髪を引っ張るように聞こえます!