Перехват управляемого исключения через собственные кадры
Возможно ли, чтобы управляемое исключение генерировалось и перехватывалось управляемым кодом, но где есть промежуточные собственные кадры в стеке вызовов?
У меня проблемы с этим. Приложение представляет собой 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
является функцией C++ в Native.exe
который называется виртуально. Короче говоря, в конечном итоге он вызывает обратный вызов в управляемый метод в Glue.dll
который поднимает MyEvent
,
Если MyEvent
и программа работает в 32-битной системе Windows XP, она ведет себя так, как задумано, и консоль покажет:
Throwing...
Caught
Работая в 64-разрядной системе Windows 7, я получаю следующее:
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
в конце концов звонит Win32 GetMessage
а потом 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, но я не знаю, было ли это хорошо определенным поведением или мне просто повезло.
Если это должно работать, каковы возможные причины, почему это не работает на всех системах?
Если это не должно работать, то, я думаю, мне придется увеличить все управляемые обратные вызовы, чтобы перехватить управляемые исключения, обернуть их родным исключением, перехватить его в управляемой оболочке для встроенной функции и выбросить исходное управляемое исключение., Звучит так, как будто много волос тянет!
1 ответ
Я имею дело с той же проблемой. У меня есть форма, код, который ее вызывает (или, скорее, код, который вызывает.ShowDialog()) находится внутри блока try { } с соответствующим блоком catch { }. В какой-то момент нажатие кнопки в диалоговом окне вызывает исключение, НО улов не ударил!
Поэтому я отредактировал код, а затем просто окружил оскорбительный оператор (преобразование в обработчике OnClick) его собственным try/catch.
Ну, "поймай", но внутри есть простой "бросок"; приводит к пользовательскому необработанному исключению!
Если я посмотрю на стек, есть несколько управляемых / собственных / управляемых переходов.
Похоже, что управляемый стек не имеет обработчика и что система не проходит через собственные кадры к следующему управляемому кадру, поэтому считает, что обработчика нет.