
Роббинс Д. - Отладка приложений для Microsoft .NET и Microsoft Windows - 2004
.pdf
ГЛАВА 7 Усложненные технологии неуправляемого кода в Visual Studio .NET |
261 |
|
|
цией, функции GetRealAddress, вы получите действительный адрес переменной. Передав это значение функции ReadDebuggeeMemoryEx, вы получите байты для типа. Вспомогательный класс хорош тем, что полностью скрывает магию получения данных из локальных и удаленных процессов отлаживаемой программы.
Листинг 7-1. Прототип экспорта и вспомогательная структура EEAddIn
/*—————————————————————————————————— —————————————————————————————————— Единственное описание надстроек Expression Evaluator AddIn,
извлеченное из примера EEAddIn
— —————————————————————————————————— —————————————————————————————————*/
typedef struct tagDEBUGHELPER |
|
|
{ |
|
|
DWORD dwVersion ; |
|
|
BOOL (WINAPI *ReadDebuggeeMemory)( struct tagDEBUGHELPER * pThis |
, |
|
DWORD |
dwAddr |
, |
DWORD |
nWant |
, |
VOID * |
pWhere |
, |
DWORD * |
nGot |
); |
// Далее, только если dwVersion >= 0x20000 |
|
|
|
|
|
DWORDLONG (WINAPI *GetRealAddress)( |
struct |
tagDEBUGHELPER *pThis ) ; |
|||
BOOL (WINAPI *ReadDebuggeeMemoryEx)( struct tagDEBUGHELPER *pThis |
, |
||||
|
DWORDLONG |
qwAddr |
, |
||
|
DWORD |
|
nWant |
, |
|
|
VOID* |
|
pWhere , |
||
|
DWORD |
* |
nGot |
); |
|
int (WINAPI *GetProcessorType)( struct tagDEBUGHELPER *pThis ) ; |
|
||||
} DEBUGHELPER ; |
|
|
|
|
|
// Прототип, которому должны соответствовать все ваши функции |
|
|
|||
typedef HRESULT (WINAPI *CUSTOMVIEWER)( |
DWORD |
|
dwAddress |
, |
|
|
DEBUGHELPER * |
pHelper |
, |
|
|
|
int |
|
nBase |
, |
|
|
BOOL |
|
bUniStrings |
, |
|
|
char * |
|
pResult |
, |
|
|
size_t |
|
max |
, |
|
|
DWORD |
|
reserved |
) ; |
Задача вашей экспортируемой функции — преобразовать эти байты, считан ные из отлаживаемой программы в нечто отображаемое в окне Watch. Так как можно легко прочитать память отлаживаемой программы, вы будете работать с копией информации. Когда я получил первое представление об архитектуре EEAdd In, я сразу представил миллионы отображаемых параметров, которые хотел бы видеть. Первый из них принимал бы HINSTANCE или HMODULE и показывал значение, за которым следовало бы имя DLL в этом местоположении. Но затем я спустился с небес. Для преобразования HINSTANCE или HMODULE в имя DLL требуется описатель процесса. Структура DEBUGHELPER (листинг 7 1) позволяет считывать память, но не получать описатель процесса. Конечно, позже я осознал, что если бы моя функ ция EEAddIn работала с процессом, отлаживаемым удаленно, то не помогло бы

262 ЧАСТЬ II Производительная отладка
даже наличие описателя процесса, поскольку я не смог бы ничего с ним сделать на машине, где запущен отладчик. Возможно, будущие версии Visual Studio .NET предложат способы запроса у процесса информации, требующей значения опи сателей.
Несмотря на ограничение, по которому можно считывать только память отла живаемой программы, вам открывается масса прекрасных вариантов наблюдае мых параметров для размещения в окне Watch, ускоряющих отладку. В примерах кода к книге содержится моя текущая надстройка EEAddIn — BSU_ExpEval_AddIn. К моменту написания этого абзаца я объединил наблюдаемые параметры _SYSTEMTIME и _FILETIME из примера Visual Studio, но добавил к ним обработку ошибок и рас ширения структур _OSVERSIONINFOA, _OSVERSIONINFOW, _OSVERSIONINFOEXA и _OSVERSIONINFOEXW. Теперь, имея одну из структур, обрабатываемых GetVersionEx, их можно отобра жать, как показано на рис. 7 5, который демонстрирует часть вывода тестовой программы для BSU_ExpEval_AddIn. В листинге 7 2 показано, что надо сделать для расширения структуры _OSVERSIONINFOA.
Один совет, касающийся DLL для EEAddIn: если из своей функции вы возвра щаете E_FAIL, в окне Watch отображается «???», поэтому лучше возвращать S_OK и устанавливать в возвращаемом тексте «…», чтобы ваш вывод совпадал с обычным отображением в окне Watch. Это может помочь и в отладке DLL. Еще советую в отладочных сборках указывать в возвращаемом тексте информацию о сбоях, чтобы ваши расширения для отладчика было легче отлаживать. И еще: стоит нам начать обмениваться своими надстройками EEAddIn, и мы сможем получить гораздо луч шую отладочную информацию, чем когда либо ранее от IDE. Призываю вас рас сматривать любые возможные структуры и классы из Win32, MFC и ATL, решая, нельзя ли предоставить улучшенный вывод.
Рис. 7 5. EEAddIns в работе
Листинг 7-2. Пример EEAddIn для _OSVERSIONINFOA
//Затрагивает только первые 5 двойных слов в структурах,
//так что можно передавать и ANSI и UNICODE версии
static int ConvertBaseOSV ( LPOSVERSIONINFOA pOSVA , char * szStr )
{
int iCurrPos = 0 ;
if ( ( pOSVA >dwMajorVersion == 4 ) && ( pOSVA >dwMinorVersion ==0))
{
if ( pOSVA >dwPlatformId == VER_PLATFORM_WIN32_NT )
{
iCurrPos = wsprintf ( szStr , _T ( "Windows NT 4.0 " ) ) ;
}
else
{

ГЛАВА 7 Усложненные технологии неуправляемого кода в Visual Studio .NET |
263 |
||
|
|
||
|
|
||
|
|
||
iCurrPos = wsprintf ( szStr , _T ( "Windows 95 " ) ) ; |
|
||
} |
|
|
|
} |
|
|
|
else if ( ( pOSVA >dwMajorVersion == 4 |
) && |
|
|
( pOSVA >dwMinorVersion == 10 ) |
) |
|
|
{ |
|
|
|
iCurrPos = wsprintf ( szStr , _T ( "Windows 98 " ) ) ; |
|
||
} |
|
|
|
else if ( ( pOSVA >dwMajorVersion == 4 |
) && |
|
|
( pOSVA >dwMinorVersion == 90 ) |
) |
|
|
{ |
|
|
|
iCurrPos = wsprintf ( szStr , _T ( "Windows Me " ) ) ; |
|
||
} |
|
|
|
else if ( ( pOSVA >dwMajorVersion == 5 |
) && |
|
|
( pOSVA >dwMinorVersion == 0 |
) |
) |
|
{ |
|
|
|
iCurrPos = wsprintf ( szStr , _T ( "Windows 2000 " ) ) ; |
|
||
} |
|
|
|
else if ( ( pOSVA >dwMajorVersion == 5 |
) && |
|
|
( pOSVA >dwMinorVersion == 1 |
) |
) |
|
{ |
|
|
|
iCurrPos = wsprintf ( szStr , _T ( "Windows XP " ) ) ; |
|
||
} |
|
|
|
else if ( ( pOSVA >dwMajorVersion == 5 |
) && |
|
|
( pOSVA >dwMinorVersion == 2 |
) |
) |
|
{ |
|
|
|
iCurrPos = wsprintf ( szStr , _T ( "Windows Server 2003 " ) ) ;
}
else
{
// Сдаюсь! iCurrPos = 0 ;
}
return ( iCurrPos ) ;
}
//Опять же, эта функция использует общее поле версий A и W,
//так что ее можно использовать для обеих.
static int ConvertBuildNumber ( LPOSVERSIONINFOA pOSVA , char * szStr )
{
int iCurrPos = 0 ;
if ( VER_PLATFORM_WIN32_NT == pOSVA >dwPlatformId )
{ |
|
iCurrPos = wsprintf ( szStr |
, |
_T ( "(%d) " ) |
, |
pOSVA >dwBuildNumber |
) ; |
}
else if ( VER_PLATFORM_WIN32_WINDOWS == pOSVA >dwPlatformId )
{
WORD wBuild = LOWORD ( pOSVA >dwBuildNumber ) ;
см. след. стр.

264 ЧАСТЬ II Производительная отладка
iCurrPos = wsprintf ( szStr , _T ( "(%d) " ) , wBuild ) ;
}
return ( iCurrPos ) ;
} |
|
|
ADDIN_API HRESULT WINAPI |
|
|
AddIn_OSVERSIONINFOA ( DWORD |
/*dwAddress*/ |
, |
DEBUGHELPER* |
pHelper |
, |
int |
/*nBase*/ |
, |
BOOL |
/*bUniStrings*/ |
, |
char * |
pResult |
, |
size_t |
/*max*/ |
, |
DWORD |
/*reserved*/ |
) |
{ |
|
|
if ( pHelper >dwVersion < 0x20000 ) |
|
|
{ |
|
|
// Я не работаю с версиями ниже VS.NET. return ( E_FAIL ) ;
}
HRESULT hRet = E_FAIL ; |
|
|
|
__try |
|
|
|
{ |
|
|
|
DWORDLONG |
dwRealAddr |
= pHelper >GetRealAddress ( pHelper ); |
|
DWORD |
nGot |
= 0 ; |
|
OSVERSIONINFOA stOSA ; |
|
|
|
// Пытаемся считать структуру. |
|
||
if ( S_OK == |
|
|
|
pHelper > |
|
|
|
|
ReadDebuggeeMemoryEx ( pHelper |
, |
|
|
|
dwRealAddr |
, |
|
|
sizeof ( OSVERSIONINFOA ) , |
|
|
|
&stOSA |
, |
|
|
&nGot |
)) |
{ |
|
|
|
// Убеждаемся, что все получено полностью. if ( nGot == sizeof ( OSVERSIONINFOA ) )
{
// Танцуем...
char * pCurr = pResult ;
int iCurr = ConvertBaseOSV ( &stOSA , pCurr ) ; if ( 0 != iCurr )
{
pCurr += iCurr ;

ГЛАВА 7 Усложненные технологии неуправляемого кода в Visual Studio .NET |
265 |
|
|
iCurr = ConvertBuildNumber ( &stOSA , pCurr ) ;
pCurr += iCurr |
; |
|
if ( '\0' != stOSA.szCSDVersion[0] ) |
|
|
{ |
|
|
wsprintf ( |
pCurr |
, |
|
_T ( "%s" ) |
, |
|
stOSA.szCSDVersion |
) ; |
} |
|
|
}
else
{
_tcscpy ( pResult , _T ( "..." ) ) ;
}
}
hRet = S_OK ;
}
}
__except ( EXCEPTION_EXECUTE_HANDLER )
{
hRet = E_FAIL ;
}
return ( hRet ) ;
}
Стандартный вопрос отладки
Решена ли проблема ограничения в 255 символов в отладке?
ДА! В Visual Studio до версии Visual Studio .NET отладочная информация для неуправляемого кода была ограничена максимумом в 255 символов. Во времена C это не представляло проблемы, но введение шаблонов совершенно захлестнуло за 255 символов даже для простейших типов. Visual Studio .NET может иметь отладочные символы произвольной длины, так что вы долж ны увидеть все полностью. Еще это означает, что старое информационное сообщение C4786 (отладочная информация длиннее 255 символов), оста навливавшее компиляцию при трактовке предупреждений как ошибок, на конец то кануло раз и навсегда! Блаженны мы!
Удаленная отладка
Удаленная отладка неуправляемых приложений действует почти так же, как и уда ленная отладка управляемых приложений. Просто установите компоненты удален ной отладки (см. главу 6) убедитесь, что ваша учетная запись на удаленном ком пьютере внесена в группы Administrators и Debugger Users, и можете подключать ся и отлаживать все что угодно через новый транспортный уровень DCOM. Это

266 ЧАСТЬ II Производительная отладка
отличный способ подключаться и отключаться от этих долгоиграющих сервер ных процессов.
В дополнение к транспортному уровню DCOM, Visual Studio .NET 2003 пред лагает еще два варианта удаленной отладки: Pipes (каналы) и TCP/IP. Вариант TCP/ IP присутствовал с Visual C++ 6, но он не так безопасен, как Pipes. Удаленная от ладка через TCP/IP позволяет любому подключиться к машине, а вот Pipes — указать, каким пользователям разрешено подключаться и отлаживать. Отладка через Pipes теперь установлена по умолчанию, хотя это медленнее чем TCP/IP.
Хотя она не так удобна как DCOM, отладка через Pipes и TCP/IP может стать прекрасным инструментом для решения отладочных задач. Одна, несомненно хорошая особенность в том, что в отладке через Pipes и TCP/IP можно запускать процессы. Кроме того, вы вправе настроить свои решения Visual Studio .NET так, чтобы они всегда запускали процесс для удаленной отладки. Это особенно полезно для «тяжелых» клиентских приложений, таких как DirectX игры. Весьма востре бована новая возможность создания нескольких подключений к удаленной машине, чтобы отлаживать несколько процессов. Еще одна прелесть в том, что, если вы со бираетесь отлаживать только неуправляемый код, вам не нужно полностью про ходить Remote Components Setup чтобы установить только отладку через Pipes и TCP/IP. Для установки отладки через Pipes и TCP/IP достаточно скопировать дво ичные файлы с компьютера, на котором установлена Visual Studio .NET, в каталог на удаленной машине. Эти двоичные файлы и их расположение на компьютере с Visual Studio .NET показаны в табл. 7 5. Помните: версию MSVCMON.EXE из Visual C++ 6 использовать с Visual Studio .NET нельзя.
Табл. 7-5. Компоненты удаленной отладки через Pipe и TCP/IP
Файл |
Расположение |
MSVCR71.DLL |
%SYSTEMROOT%\SYSTEM32 |
MSVCI71.DLL |
%SYSTEMROOT%\SYSTEM32 |
MSVCP71.DLL |
%SYSTEMROOT%\SYSTEM32 |
MSVCMON.EXE |
<Установочный каталог Visual Studio |
|
.NET>\COMMON7\PACKAGES\DEBUGGER |
NATDBGDM.DLL |
<Установочный каталог Visual Studio |
|
.NET>\COMMON7\PACKAGES\DEBUGGER |
NATDBGTLNET.DLL |
<Установочный каталог Visual Studio |
|
.NET>\COMMON7\PACKAGES\DEBUGGER |
|
|
Прежде чем начать удаленную отладку, хорошо бы заняться планированием, дабы обеспечить успешность сеанса удаленной отладки. Версии удаленной отладки через Pipes и TCP/IP в Visual Studio .NET не столь темпераментны, как версия Visual C++ 6. Главный фокус в том, чтобы Visual Studio .NET обнаружила символы на локальной машине, где они загружаются. Для символов ОС лучшее решение — в установке сервера символов (см. главу 2). Если вы работаете с локальной сборкой проекта, лучше, чтобы отлаживаемая программа была установлена в одинаковых каталогах на локальной и удаленной машинах, так как не возникнет путаницы с расположением отдельных элементов. Наконец, неплохо убедиться, что вы смо
ГЛАВА 7 Усложненные технологии неуправляемого кода в Visual Studio .NET |
267 |
|
|
жете запустить свою программу на удаленной машине, ведь нет ничего хуже, чем, начав удаленную отладку, обнаружить что не хватает DLL.
Чтобы начать удаленную отладку через подключения Pipe, надо зарегистриро ваться в системе на удаленной машине и запустить MSVCMON.EXE. По умолчанию запуск MSVCMON.EXE подразумевает подключение к удаленной машине с маши ны, на которой запущена IDE Visual Studio .NET, с применением той же учетной записи, под которой вы зарегистрировались на удаленной машине. Если вы хо тите сделать удаленную машину несколько более доступной, запустите MSVCMON.EXE с ключом командной строки u <домен\группа или пользователь>, чтобы указать пользователей или группы, которым вы хотите разрешить запускать и отлаживать процессы на этой машине.
Настроить машину, на которой запущена IDE Visual Studio .NET, очень просто. Для этого лишь надо установить несколько элементов на странице свойств Debug ging в окне свойств проекта. В разделе Action, поле Command and Working Directory следует заполнить расположением каталогов на удаленной машине. Дополнительно можно указать, что вы хотите подключиться к удаленному процессу, установив Attach в Yes. Последнее, что можно настроить в разделе Action, — это поле Symbol Path, если двоичные файлы не находятся в одинаковых каталогах на обеих ма шинах.
Вразделе Remote Settings установите Connection to Remote Via Pipe (Native Only).
Вполе Remote Machine укажите имя или IP адрес машины, хранящей MSVCMON.EXE. Можете попробовать указать имя, но IP адрес работает безотказно. Неплохо бы проверить подключение к удаленной машине с помощью PING.EXE, чтобы опре делить, доступна ли она. Если вы можете получить доступ к удаленной машине по ее имени, можете использовать имя, но IP адрес работает безотказно. Наконец, поле Remote Command должно содержать тот же полный путь и имя, которые указаны в поле Command из раздела Action. На рис. 7 6 показан пример проекта с заполненными полями.
Заполнив поля, вы быстро узнаете, установлено ли хорошее подключение. Консольное окно, в котором запущен MSVCMON.EXE, покажет имя пользователя, осуществляющего подключение, и вы начнете отладку, как обычно. Если возник нет проблема, вы узнаете что нужно сделать, чтобы устранить ее, так как со общения об ошибках в Visual Studio .NET гораздо лучше, чем в предыдущих версиях.
Если вы занимаетесь отладкой на машине, где работает сервер терминала и выполнять удаленную отладку через Pipes могут несколько пользователей, ключ MSVCMON.EXE –s <суффикс> позволит указать уникальный суффикс для именован ного канала (named pipe). Поскольку первый пользователь, начинающий удален ную отладку через Pipes, получает стандартное имя канала, следующие пользова тели, которые выполняют отладку на той же машине, должны будут указать уни кальный идентификатор для экземпляра MSVCMON.EXE, к которому они хотят подключиться. Запустив MSVCMON.EXE с параметром –s, укажите суффикс в поле Remote Machine на странице свойств Debugging диалогового окна свойств проек та, добавив суффикс к имени машины и разделив их знаком #. Так, если вы запу стили MSVCMON –s pam на машине ZENO, надо указать имя машины как ZENO#pam.

268 ЧАСТЬ II Производительная отладка
Рис. 7 6. Проект, настроенный для отладки через Pipes
Как я сказал, отладка через Pipes медленнее, хоть и безопаснее, чем отладка через TCP/IP. Если вам нужна скорость, можете включить отладку через TCP/IP с помо щью ключа командной строки tcpip. Чтобы уведомить о желании использовать TCP/IP, на странице свойств Debugging диалогового окна свойств проекта выбе рите Remote Via TCP/IP (Native Only) в разделе Remote Settings.
Для TCP/IP у MSVCMON.EXE есть несколько специальных параметров команд ной строки, которые, возможно, вас заинтересуют. С помощью первого — anyuser — вы можете позволить любому подключиться к машине без защиты, второй —
–maxsessions — указывает максимальное число сеансов отладки, допустимых в каж дый момент времени, а третий — –timeout — сообщает MSVCMON.EXE, как долго вы разрешаете ждать подключения до того, как время истечет.
Советы и уловки
В этом разделе я хочу рассказать о некоторых советах и уловках, необходимых в большинстве случаев отладки неуправляемого кода.
Отладка внедренного кода
Одна из особенностей Visual C++ .NET — новая программная модель с атрибута ми. Она способна намного облегчить разработку COM, так как позволяет комби нировать IDL атрибуты с исходным файлом, так что для создания COM объекта требуется только один файл. Если хотите посмотреть на реальный пример COM программирования с атрибутами, взгляните на объект Tester из главы 16. Кроме того, программирование с атрибутами предлагает интегрированный спо соб унифицированной обработки сообщений (unified message handling) для ва ших приложений. Все атрибуты действуют путем внедрения исходного кода в ваш исходный файл.
Есть несколько способов отладки этого внедренного кода. Чтобы увидеть ис ходный код, находясь в отладчике, перейдите в окно Disassembly, щелкните пра вой кнопкой и выберите из контекстного меню команду Show Source Code. Но проще всего увидеть, что происходит с внедренным кодом, — провести компиля
ГЛАВА 7 Усложненные технологии неуправляемого кода в Visual Studio .NET |
269 |
|
|
цию с ключом /Fx для CL.EXE. Его можно включить из среды Visual Studio, открыв диалоговое окно Property Pages, развернув папку C/C++, выбрав страницу свойств Output Files и установив Expand Attributed Source в Yes. Это приведет к созданию файла sourcename.MRG.CPP (где sourcename — имя исходного файла) в том же каталоге, где находится CPP файл. Открыв этот файл, вы увидите внедренный ис ходный код (код слияния). При желании вы вправе также откомпилировать файл слияния, чтобы при отладке увидеть в окне исходного кода, как все это работает.
Окно Memory и автоматическое обновление
Серьезные усовершенствования в отладке неуправляемого кода представляет окно Memory. Одно из них — их больше, но главное, в окне Memory больше нет таин ственного Искусственного Интеллекта, следящего за движением ваших глаз, что бы понять, на какой адрес вы смотрели, и сдвинуть его в следующий раз, когда вы посмотрите на окно Memory. Оно также получило все виды дополнительных фор матов отображения памяти, так что у вас не останется проблем с представлением памяти в том виде, который вам нужен. Чтобы выбрать формат отображения, щелкните правой кнопкой в окне Memory.
Наконец, окно Memory облегчает автоматическое обновление при изменени ях в просматриваемых блоках памяти. В окне Memory щелкните кнопку справа от адресного поля, и отладчик будет обновлять окно Memory, отображая последние значения. Это особенно ценно при работе с ESP (указателем стека), чтобы наблю дать за стеком по мере его изменения.
Контроль исключений
Одна из серьезнейших угроз производительности неуправляемых приложений — ненужные исключения. Поскольку исключения в неуправляемом коде требуют перехода в режим ядра при каждой инициации, их следует избегать любой це ной. Хотя переход из пользовательского режима в режим ядра выполняется от носительно быстро, вся дополнительная работа, связанная с обработкой исклю чения в режиме ядра, занимает массу времени. Чтобы помочь сгладить эти про блемы с производительностью, окно Exception отладчика Visual Studio .NET по зволяет контролировать обработку отладчиком любых исключений. Правильно поняв, как использовать это окно, вы сможете быстрее отслеживать ненужные исключения.
Прежде чем приступить к обсуждению диалогового окна Exception, я должен объяснить, что происходит, когда отладчик неуправляемого кода сталкивается с исключением. При возникновении исключения ОС приостанавливает процесс (т. е. все потоки останавливаются), указывает на точку возникновения исключения и уведомляет отладчик о его возникновении. Это первый случай исключения (first chance exception), поскольку здесь отладчик впервые получает возможность об работать его. У отладчика есть два пути: он может обработать исключение, и от лаживаемая программа ничего о нем не узнает, или он может передать исключе ние отлаживаемой программе. Мысль о том, что отладчик может обработать (или поглотить) исключение, может показаться странной. Однако, как вы видели в гла ве 4, установка точки прерывания в неуправляемом коде влечет за собой установ

270 ЧАСТЬ II Производительная отладка
ку в этом месте команды INT 3 . В случае с точкой прерывания отладчик иниции рует исключение в отлаживаемой программе, так что отладчик должен обраба тывать эти исключения. Если исключение инициировано не отладчиком, он со общает ОС, что не желает обрабатывать исключение, и оно передается отлажива емой программе. Отладчик также выводит сообщение в окне Output, указываю щее на возникновение первого случая исключения. Отлаживаемое приложение вновь запускается, и, если в нем установлена обработка исключений, исключение обрабатывается, и отлаживаемая программа радостно продолжает выполнение. Если в отлаживаемом приложении не установлена обработка исключений, исключение передается конечным обработчикам исключений (final exception handlers) из NTDLL.DLL. На этом этапе ОС вновь приостановит отлаживаемое приложение и сообщит отладчику, что возник второй случай исключения. Это значит, что про цесс будет завершен из за необрабатываемого исключения.
По обработке исключений в неуправляемом коде важно отметить, что, когда вы видите сообщение о первом случае исключения в окне Output, в вашем про цессе инициировалось исключение. Как я сказал, исключения представляют угрозу производительности, поэтому если при выполнении процесса вы видите множе ство сообщений «First chance exception at…», то у вас проблемы с производитель ностью. Коварство в том, что обработка исключений C++ реализована через ме ханизм структурной обработки исключений (SEH) за кадром, поэтому примене ние исключений C++ может свести производительность на нет. Исключения пред назначены для исключительных условий. В общем случае следует избегать исклю чений C++ в неуправляемых приложениях.
Чтобы отследить проблемы с производительностью, вызванные исключения ми, можно искать исключения при анализе кода. Однако для большой кодовой базы это может стать жуткой задачей. Диалоговое окно Exceptions в Visual Studio .NET (доступное из меню Debug или нажатием Ctrl+Alt+E при использовании сочета ний клавиш по умолчанию) может сделать остановку прямо в месте возникнове ния исключения и поиск места его обработки тривиальной задачей (рис. 7 7).
Рис. 7 7. Диалоговое окно Exceptions