Ошибка доступа к нарушениям с необработанными управляемыми исключениями в управляемом приложении С++.NET
Это действительно проблема, но она настолько эзотерична, что я решил поделиться ею для других пользователей.
Возможно, другие могут предложить причины?
В любом случае, я работаю над "смешанным режимом".NET-приложения, написанным на управляемом С++, но с тяжелыми ссылками на существующие родные библиотеки.
Проблема заключалась в том, что необработанные управляемые исключения закончились как Нарушения доступа Win32. Я имею в виду, что вместо того, чтобы показывать хороший диалог .NET, вы получаете необработанное управляемое исключение, вместо этого я бы получил более старый стиль "Необработанное событие win32 exception произошло в...".
Вот интересная вещь: если я запустил приложение в отладчике, то заброшенное управляемое исключение попадет правильно. то есть отладчик показывает мне строку.
Однако при нормальной работе он превратится в это нарушение доступа. Прикрепление отладчика в этот момент создавало бы небольшую полезную информацию (это даже не показало бы разумной трассировки стека).
Итак, для меня это говорит о том, что что-то происходит в собственном коде незадолго до того, как необработанное управляемое исключение попадает в обработчик исключений.
Так или иначе, мне удалось решить проблему, сравнив мой проект с чистым новым проектом на С++, сгенерированным Visual Studio 2008.
Исправление должно состоять в следующем:
-
Измените флаг /SUBSYSTEM (свойства проекта → Linker- > System- > SubSystem) от/SUBSYSTEM: WINDOWS до "Не задано"
-
Переключение с использованием старого стиля WinMain() на использование нового стиля main().
то есть. это было
int APIENTRY _tWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
И теперь
int main(array<System::String ^> ^args)
[Почему я использую этот странный _tWinMain? Это то, что было создано Visual Studio.NET IDE много лет назад, когда вы создаете образец приложения с смешанным режимом Windows. Он всегда работал нормально (до сих пор), поэтому я никогда не беспокоился об этом. _tWinMain - это просто макрос для WinMain]
Я сделал это изменение, и проблема исчезла. Необработанные исключения .NET теперь получают правильную ловушку, поэтому я могу теперь их отлаживать.
Я также сделал обратное изменение для чистого примера С++-приложения и доказал, что это причина.
Итак, на самом деле мои вопросы: что происходит?
Было ли это просто, что я использовал старый стиль WinMain вместо нового "main (array ^)"?
Должен ли я сообщить об этом Microsoft (кому-нибудь это понравится;-))?
Ответы
Ответ 1
Я не очень удивлен.
Если вы напишете верхний уровень main
в управляемом коде, любое управляемое исключение, которое будет пузыриться, будет обрабатываться управляемым кодом. То же, что и в С#. Это связано с тем, что ОС не вызывает прямо в вашей функции int main(array<System::String ^> ^args)
. Это делается с помощью управляемого кода, который является либо частью .net, либо вставлен во время компиляции. Это код, который замечает и обрабатывает любые исключающие исключения .net.
Если вы пишете собственный собственный WinMain
, в эту функцию не входит код .net, поэтому нет управляемого кода, способного обрабатывать управляемые исключения. Когда управляемое исключение выбрасывается, оно появляется в ОС как любое другое собственное исключение Windows.
Обратите внимание, что это не точное описание того, как вызывается main/WinMain. Это обоснованное предположение, основанное на вашем описании проблемы, и небольшой опыт. Я оставил некоторые детали, о которых я знаю, и, возможно, также детали, о которых я совершенно не знаю. Но я вполне уверен, что это суть истории.
Ответ 2
У меня нет опыта с этим в .NET или управляемом коде, но у меня довольно много опыта с родной стороной.
Я не вижу, как или почему изменение этой точки входа (main vs WinMain) или подсистемы (win32 vs console vs none) должно повлиять на это. Я не говорю, что это не так, просто это не первопричина. (Кроме того: я не знаю, что такое подсистема: "не установлено". Я думаю, что подсистема, которая является лингером, знает и помещает в исполняемый файл, должна быть установлена на что-то, и если точкой входа является main(), возможно, он настроен на консоль. Приложения подсистемы консоли все еще могут взаимодействовать с графическим интерфейсом всеми обычными способами, но у них также всегда есть консольное окно, и если вы запускаете их извне окна консоли, они создают один, поэтому это обычно не лучший выбор для приложений, которые вы собираетесь отправлять кому угодно, кроме вас самих.)
На уровне API Win32 этот тип поведения контролируется:
Возможно, что-то в вашем старом WinMain() или что-то вызванное оттуда или какой-то .NET управляемый goo, который работает до WinMain (даже в родном C/С++ с установкой MSVC по умолчанию WinMain не является реальной точкой входа что касается компоновщика - существует функция-оболочка, предоставленная библиотекой времени выполнения MSVC C, которая является реальной точкой входа и которая вызывает WinMain - я ожидал бы, что .NET сделает то же самое, но более того), вызывал SetUnhandledExceptionFilter (NULL), и в вашей фиксированной версии, которая больше не происходит.
Если вы знаете, какая функция отвечает за создание "хорошего .NET-диалога, которое получает с необработанным управляемым исключением", вы можете передать это в SetUnhandledExceptionFilter(), но, вероятно, не рекомендуется называть это для исключений в неуправляемый код.
Альтернативная теория для всего этого: "старый стиль" Необработанное исключение win32 произошло в диалоговом окне... "Фактически сказал" Нарушение прав доступа" и это означало? Также возможно, что обработчик исключений .NET был взломан, а затем по какой-то причине рушился; если в этом случае это можно было бы отладить, хотя для этого потребуется гораздо больше информации.