Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Роббинс Д. - Отладка приложений для Microsoft .NET и Microsoft Windows - 2004

.pdf
Скачиваний:
353
Добавлен:
13.08.2013
Размер:
3.3 Mб
Скачать

ГЛАВА 13 Обработчики ошибок

503

 

 

вы желаете видеть в итоговой строке. Если включены все флаги, будет выведено что то вроде:

001B:004017FD (0x00000001 0x00000000 0x00894D00 0x00000000)

CrashHandlerTest.exe, wmain()+1407 byte(s), d:\dev\booktwo\disk\bugslayerutil\tes

ts\crashhandler\crashhandler.cpp,

line 0226+0007 byte(s)

В скобках выводятся первые четыре возможных параметра функции. Список флагов приведен в табл. 13 1. Некоторые из вас, возможно, удивляются, почему в качестве одного из вариантов я не включил вывод информации о локальных пе ременных. Это объясняется двумя причинами. Во первых, CrashHandler предназ начен прежде всего для использования клиентами. Если вы не желаете выдавать секреты, вы, вероятно, не предоставляете своим клиентам PDB файлы с частной информацией. Во вторых, локальные переменные, особенно в расширенном виде, занимают довольно большой объем памяти. Я и так чувствовал, что приближаюсь к пределам возможностей из за статических буферов, поэтому решил, что описа ние локальных переменных будет чрезмерным.

Табл. 13-1. Флаги GetFirstStackTraceString и GetNextStackTraceString

Флаг

Выводимая информация

0

Только адрес стека

GSTSO_PARAMS

Первые четыре возможных параметра

GSTSO_MODULE

Имя модуля

GSTSO_SYMBOL

Имя символа для адреса стека

GSTSO_SRCLINE

Информация об исходном файле и номере строки для адреса стека

 

 

Чтобы показать функции GetFirstStackTraceString и GetNextStackTraceString в

действии, я прилагаю к этой книге две тестовых программы: BugslayerUtil\Tests\ CrashHandler выполняет методы CrashHandler, а CrashTest отображает пример ди алогового окна, которое вы можете вывести при необработанной ошибке. Благо даря этим двум программам вы должны получить достаточно хорошее представ ление о том, как использовать представленные мной функции. На рис. 13 2 пока зано окно сообщения об ошибке, выводимое программой CrashTest.

Минидампы

Возможно, вы удивляетесь, зачем я продолжаю разработку и сопровождение кода для манипулирования структурами EXCEPTION_POINTERS в библиотеке CrashHandler, потому что вы много слышали о минидампах. Я делаю это главным образом по тому, что не хочу нарушать совместимость моего кода со многими программами, в которых он уже используется. Однако возможности минидампов настолько уди вительны, что я уверен, что многие программисты просто заменят код своих об работчиков ошибок вызовами функций создания минидампов, причем сделают это так быстро, как только смогут.

Я уже объяснял, как читать файлы минидампов при помощи Microsoft Visual Studio .NET и WinDBG в главах 7 и 8 соответственно. Теперь я хочу рассказать, как создавать собственные минидампы прямо из своей программы. Я считаю, что API

504 ЧАСТЬ IV Мощные средства и методы отладки неуправляемого кода

Рис. 13 2. Диалоговое окно программы CrashTest

минидампов — самое полезное средство, предоставленное Microsoft разработчи кам неуправляемого кода за последние несколько лет после технологии серверов символов и самой Visual Studio .NET! Однако создание собственных минидампов имеет свои тонкости, поэтому я покажу вам, как получать самые лучшие мини дампы, что позволит вам значительно ускорить исправление ошибок.

API-функция MiniDumpWriteDump

Всю работу по созданию минидампов выполняет функция MiniDumpWriteDump, со держащаяся в DBGHELP.DLL версии 5.1 или более поздней. Это значит, что все версии этой библиотеки из Microsoft Windows 2000 (до Service Pack 3 включительно) эту функцию не экспортируют. Кроме того, в MiniDumpWriteDump из библиотек DBGHELP.DLL до версии 6.0 есть ошибка, приводящая к взаимной блокировке при записи минидампа из текущего процесса. К счастью, эти версии библиотек рас пространялись только с Debugging Tools for Windows, поэтому их не должно быть на машинах пользователей. DBGHELP.DLL уже не имеет ограничений на распрос транение, поэтому, чтобы гарантировать своим приложениям благополучную жизнь, включайте в их состав DBGHELP.DLL 6.1.17.1 или ее более позднюю вер сию и устанавливайте ее в каталог своей программы, но не в каталог %SYSTEM ROOT%\System32. DBGHELP.DLL входит в пакет Debugging Tools for Windows (т. е. WinDBG), который вы можете найти на CD. Чтобы получить последнюю версию DBGHELP.DLL, зайдите на сайт http://www.microsoft.com/ddk/debugging/ и загру зите Debugging Tools for Windows. После установки всех компонентов вы сможе те скопировать DBGHELP.DLL из каталога Debugging Tools for Windows.

В следующем фрагменте представлен прототип MiniDumpWriteDump. Имена пара метров говорят сами за себя, однако я хотел бы кое что отметить. Первый пара метр, описатель процесса, должен иметь права на чтение и запрос. Так как мно

ГЛАВА 13 Обработчики ошибок

505

 

 

гие наши программы выполняются без прав администратора, вам, возможно, при дется поработать с параметрами безопасности, чтобы гарантировать права на вызов MiniDumpWriteDump, если вы заимствуете атрибуты безопасности (impersonation) или иным образом манипулируете атрибутами пользователей и прав. Однако в самых разнообразных типах приложений, в которых я применял MiniDumpWriteDump, мне ни разу не приходилось изменять атрибуты безопасности описателя процесса.

BOOL MiniDumpWriteDump ( HANDLE

hProcess

,

DWORD

ProcessId

,

HANDLE

hFile

,

MINIDUMP_TYPE

DumpType

,

PMINIDUMP_EXCEPTION_INFORMATION

ExceptionParam ,

PMINIDUMP_USER_STREAM_INFORMATION

UserStreamParam,

PMINIDUMP_CALLBACK_INFORMATION

CallbackParam );

Четвертый параметр — тип дампа, который вы хотите записать. Похоже, это перечисление изменяется с каждой версией Debugging Tools for Windows, поэто му убедитесь, что у вас установлена соответствующая версия Debugging Tools for Windows и установите компоненты SDK для получения последнего заголовочно го файла DBGHELP.H. Из документации не совсем ясно, что флаги перечисления MINIDUMP_TYPE можно объединять, запрашивая запись в дамп дополнительной ин формации. Что бы вы ни делали, всегда устанавливайте флаг MiniDumpWithHandle Data для получения информации об описателях.

Если вы работаете над приложениями, к безопасности которых предъявляют ся повышенные требования, или ваши клиенты очень обеспокоены защитой дан ных, знайте, что MiniDumpWriteDump может вывести информацию, которую выводить не следовало бы. Для защиты пользователей Microsoft включила в DBGHELP.DLL 6.1.17.1 и более поздние ее версии два флага: MiniDumpFilterMemory и MiniDump FilterModulePaths. Первый удаляет из дампа частные данные, которые не требуют ся для анализа стека, а второй исключает из путей к модулям имена пользовате лей и важные имена каталогов. Флаг MiniDumpFilterModulePaths очень полезен, од нако он может затруднить нахождение модулей в минидампе.

Для нас также представляет интерес пятый параметр — ExceptionParam. Для до бавления в минидамп информации об ошибке ему нужно присвоить значение указателя на структуру EXCEPTION_POINTERS. В качестве двух последних параметров вы почти всегда будете передавать NULL. Тем не менее параметр UserStreamParam может пригодиться, если вам захочется записать в минидамп собственную информацию: состояние программы, предпочтения пользователей, список объектов и все, что ваша душа пожелает. Чтение пользовательских данных при помощи функции MiniDumpReadDumpStream вам придется выполнять самому, но я могу вас обрадовать: содержание минидампа при этом будет ограничено только вашим воображением.

Укрощение MiniDumpWriteDump

Увидев MiniDumpWriteDump, я сразу же понял, что я должен написать для нее функ цию оболочку. Это было нужно, во первых, чтобы скрыть функцию GetProcAddress, так как я хотел гарантировать работу моего кода под управлением имевшейся версии Windows 2000, и во вторых, чтобы избежать необходимости открытия файла

506 ЧАСТЬ IV Мощные средства и методы отладки неуправляемого кода

перед каждым вызовом MiniDumpWriteDump. Создав первую версию простой оболоч ки, я понял, что мне никогда больше не придется ничего делать, кроме установки параметра ExceptionParam для указания на обрабатываемую при ошибке структуру EXCEPTION_POINTERS,. Моя функция записи минидампа из вашей функции обработ чика ошибок называется CreateCurrentProcessCrashDump. Я также написал функцию

IsMiniDumpFunctionAvailable, которая возвращает TRUE, если MiniDumpWriteDump при сутствует в адресном пространстве. Вы можете найти обе функции в файле MINI DUMP.CPP утилиты BugslayerUtil.

Все шло отлично, пока однажды я не решил написать функцию, которая запи сывала бы минидамп в любой момент выполнения программы, а не только при ошибке. Мы работали над серверным приложением, и нам хотелось иметь воз можность записи минидампа при конкретном внешнем событии. Благодаря это му мы могли бы изучать состояние приложения постфактум, не подключая к ком пьютеру отладчик. Увы, минидампы, создаваемые MiniDumpWriteDump, не всегда были удобочитаемы.

Отображая эти минидампы, WinDBG всегда выводил нечто, выглядевшее как фальшивый стек вызовов. Visual Studio .NET работала лучше, но иногда показыва ла странные плавающие стеки, даже когда у меня повсюду были отличные симво лы. Почесав немного затылок, я понял, в чем дело. MiniDumpWriteDump записывала стек вызовов для собственного потока, начинающийся глубоко внутри самой MiniDumpWriteDump. Хотя у меня и были отличные символы, изучать код было очень сложно.

Так как я записывал дамп, а не обрабатывал ошибку, я несколько недоумевал. Все файлы дампов, которые я записывал при ошибке, были прекрасно сформи рованы и читались обоими отладчиками. Конечно, чтобы WinDBG читал настоя щие файлы дампа ошибки, я должен был использовать команды .ecxr;kp для со здания структуры информации об исключении и просмотра стека. Я подумал, что для получения файла минидампа с хорошими стеками вызовов можно реализо вать похожую идею: подделать структуру MINIDUMP_EXCEPTION_INFORMATION, заполнив ее теми же значениями, которые имеют место при ошибке.

Вся проблема подделки структуры MINIDUMP_EXCEPTION_INFORMATION сводится к занесению правильной информации о регистрах в структуру CONTEXT, чтобы под дельная ошибка казалась отладчикам настоящей. После многих проб и ошибок я написал функцию SnapCurrentProcessMiniDump. Теперь при получении минидампа в любое время всегда будет выполняться правильный анализ стека. Вам, возможно, захочется изучить код листинга 13 5, потому что он работает довольно интересно.

Первая проблема заключалась в том, откуда брать регистры и какое значение надо присваивать адресу исключения. В конце концов я решил, что мне нужны те же значения регистров, которые имеют место при вызове моей функции SnapCurrent ProcessMiniDump. Чтобы получить правильные значения регистров, я должен был добраться до них раньше кода, сгенерированного компилятором, поэтому я ис пользовал соглашение вызова naked.

В итоге я создал структуру CONTEXT в стеке и написал на встроенном языке ас семблера копирование регистров в соответствующие поля структуры. Первую ошибку я допустил сам, потому что копировал 16 разрядные сегментные регист ры в поля CONTEXT, упустив из вида, что поля для этих сегментов были 32 разряд

ГЛАВА 13 Обработчики ошибок

507

 

 

ными. В результате я оставлял мусор в старших словах полей. Для исправления этой ошибки мне пришлось сначала копировать сегментные регистры в EAX и потом сохранять его в полях структуры. Значения EBP и ESP нужно было находить иначе, потому что с их помощью в начале функции создавался кадр стека, но и это не вызвало проблем. В структуру заносятся те же значения ESP и EBP, какими они были во время вызова SnapCurrentProcessMiniDump. Макрос SNAPPROLOG в листинге 13 5 представляет собой пролог, а SNAPEPILOG — эпилог, необходимые для функций с соглашением вызова naked. Единственный регистр, значение которого не заносится в CONTEXT во время пролога, — это регистр EIP, для которого потребовалось чуть больше работы.

Я думал, что регистров общего назначения хватит, но все равно не исключа лась возможность того, что для правильного отображения пользовательской ин формации отладчику нужны и другие регистры, например, регистры для работы с числами с плавающей точкой или дополнительные регистры. Поэтому я решил получить и их значения, вызвав GetThreadContext для выполняющегося потока. Так как мой код не изменяет эти регистры, я получаю их действительные значения в момент вызова. Конечно, я передаю в функцию GetThreadContext адрес другой струк туры CONTEXT, иначе я просто перезаписал бы уже сохраненные значения регист ров. После получения регистров через GetThreadContext я копирую в итоговую струк туру значения, сохраненные мной ранее в первой структуре.

Итак, я получил корректные значения регистров, имевшие место при вызове SnapCurrentProcessMiniDump. Сначала я хотел поместить в стек адрес возврата и как значение регистра EIP, и как адрес исключения. Получить адрес возврата уже просто, так как Microsoft задокументировала внутреннюю функцию _ReturnAddress. С ее помощью вы можете получить адрес возврата из любого места функции. Чтобы задействовать _ReturnAddress, вам надо добавить в свой код две следующих стро ки, чтобы компилятор не жаловался, что функция не определена. Я предпочитаю размещать эти строки в прекомпилированном заголовочном файле, чтобы они были доступны глобально.

extern "C" void * _ReturnAddress ( void ) ;

#pragma intrinsic ( _ReturnAddress )

Благодаря тому, что остальные регистры у меня имеют те же значения, какие имели до вызова, очень немногие люди заметили бы разницу, если б я просто использовал адрес возврата как значение EIP и адрес исключения. Однако я по ступил более предусмотрительно, изучив значения, отстоящие на несколько байт от адреса возврата, на предмет наличия идентификаторов операций 0xE8 и 0xFF, определяющих команды ближнего и дальнего вызовов соответственно. После поправки все регистры имеют абсолютно правильные значения, такие же, как и при вызове функции. Определение типа CALL вы можете увидеть в CalculateBegin ningOfCallInstruction.

Остальные действия после получения нужных регистров заключаются в запол нении структуры MINIDUMP_EXCEPTION_INFORMATION, открытии описателя файла и вы зове MiniDumpWriteDump. Все это вы можете увидеть в функции CommonSnapCurrent ProcessMiniDump в листинге 13 5.

508 ЧАСТЬ IV Мощные средства и методы отладки неуправляемого кода

Открытие файла, созданного в SnapCurrentProcessMiniDump, аналогично откры тию любого другого минидампа. Единственное различие в том, что отладчик со общит номер исключения как 0 и покажет указатель команд на самой команде CALL. Теперь вы никак не оправдаете отсутствия минидампов. Создайте фоновый поток, ожидающий нужное событие, и при возникновении этого события во внешней программе вызывайте в своем потоке SnapCurrentProcessMiniDump для получения отличных дампов на всем протяжении выполнения своей программы.

Листинг 13-5. SnapCurrentProcessMiniDump и ее друзья из файла MINIDUMP.CPP

//Ниже приведены фрагменты из файла MINIDUMP.CPP, иллюстрирующие

//работу функции SnapCurrentProcessMiniDump.

//Расстояние (в байтах) от адреса возврата до команд

//ближнего и дальнего вызовов. Эти значения используются

//в функции CalculateBeginningOfCallInstruction.

#define k_CALLNEARBACK 5 #define k_CALLFARBACK 6

//Общий пролог для функций SnapCurrentProcessMiniDumpA и

//SnapCurrentProcessMiniDumpW, использующих соглашение naked.

#define SNAPPROLOG(Cntx)

 

\

__asm PUSH

EBP

 

/* Явное сохранение регистра EBP. */ \

__asm MOV

EBP , ESP

 

/* Формирование кадра стека.

*/ \

__asm SUB

ESP , __LOCAL_SIZE

/* Место для локальных переменных.*/ \

/* Копирование значений всех легкодоступных регистров. */

\

__asm MOV

Cntx.Eax ,

EAX

 

\

__asm MOV

Cntx.Ebx ,

EBX

 

\

__asm MOV

Cntx.Ecx ,

ECX

 

\

__asm MOV

Cntx.Edx ,

EDX

 

\

__asm MOV

Cntx.Edi ,

EDI

 

\

__asm MOV

Cntx.Esi ,

ESI

 

\

/* Я обнуляю весь регистр EAX, но записываю значения сегментов

*/ \

/* только в его младшее слово. Это гарантирует правильную

*/ \

/* инициализацию старших слов полей сегментных регистров

*/ \

/* в структуре CONTEXT, так как на самом деле они 32 разрядные.

*/ \

__asm XOR

EAX , EAX

 

 

\

__asm MOV

AX , GS

 

 

\

__asm MOV

Cntx.SegGs

, EAX

 

\

__asm MOV

AX , FS

 

 

\

__asm MOV

Cntx.SegFs

, EAX

 

\

__asm MOV

AX , ES

 

 

\

__asm MOV

Cntx.SegEs

, EAX

 

\

__asm MOV

AX , DS

 

 

\

__asm MOV

Cntx.SegDs

, EAX

 

\

__asm MOV

AX , CS

 

 

\

__asm MOV

Cntx.SegCs

, EAX

 

\

__asm MOV

AX , SS

 

 

\

__asm MOV

Cntx.SegSs

, EAX

 

\

/* Получение предыдущего значения EBP. */

\

 

ГЛАВА 13

Обработчики ошибок

509

 

 

 

 

 

 

 

 

 

 

 

 

__asm MOV

EAX , DWORD PTR [EBP]

\

 

__asm MOV

Cntx.Ebp , EAX

\

 

/* Получение предыдущего значения ESP. */

\

 

__asm MOV

EAX , EBP

\

 

/* Предыдущее значение регистра ESP на два

*/ \

 

/* двойных

слова превышает значение EBP.

*/ \

 

__asm ADD

EAX , 8

\

 

__asm MOV

Cntx.Esp , EAX

\

 

/* Сохранение изменяемых регистров. */

\

 

__asm PUSH

ESI

\

 

__asm PUSH

EDI

\

 

__asm PUSH

EBX

\

 

__asm PUSH

ECX

\

 

__asm PUSH

EDX

 

 

//Общий эпилог для функций SnapCurrentProcessMiniDumpA и

//SnapCurrentProcessMiniDumpW, использующих соглашение naked.

#define SNAPEPILOG(eRetVal)

 

 

\

__asm POP

EDX

/* Восстановление

 

*/ \

__asm POP

ECX

/* сохраненных регистров.

 

*/ \

__asm POP

EBX

 

 

\

__asm POP

EDI

 

 

\

__asm POP

ESI

 

 

\

__asm MOV

EAX , eRetVal

/* Возвращаемое значение.

 

*/ \

__asm MOV

ESP , EBP

/* Восстановление указателя стека.

*/ \

__asm POP

EBP

/* Восстановление регистра EBP.

*/ \

__asm RET

 

/* Возврат в вызвавшую функцию.

*/

BSUMDRET CommonSnapCurrentProcessMiniDump ( MINIDUMP_TYPE eType

,

 

 

LPCWSTR

szDumpName ,

 

 

PCONTEXT

pCtx

)

{

 

 

 

 

// Надеемся на лучшее.

 

 

 

BSUMDRET eRet = eDUMP_SUCCEEDED ;

 

 

//Пытался ли я уже получить экспортируемую ф цию MiniDumpWriteDump? if ( ( NULL == g_pfnMDWD ) && ( eINVALID_ERROR == g_eIMDALastError))

{

if ( FALSE == IsMiniDumpFunctionAvailable ( ) )

{

eRet = g_eIMDALastError ;

}

}

//Если указатель на MiniDumpWriteDump равен NULL, выполняется выход. if ( NULL == g_pfnMDWD )

{

eRet = g_eIMDALastError ;

}

if ( eDUMP_SUCCEEDED == eRet )

см. след. стр.

510 ЧАСТЬ IV Мощные средства и методы отладки неуправляемого кода

{

//Вооружившись контекстом, имевшим место во время вызова

//этой функции, я могу заняться действительной записью дампа.

//Чтобы все работало должным образом, мне нужно создать

//впечатление, что произошло исключение. Для этого надо

//заполнить структуру MINIDUMP_EXCEPTION_INFORMATION.

EXCEPTION_RECORD stExRec ;

EXCEPTION_POINTERS stExpPtrs ;

MINIDUMP_EXCEPTION_INFORMATION stExInfo ;

//Обнуление всех отдельных значений структур. ZeroMemory ( &stExRec , sizeof ( EXCEPTION_RECORD )) ;

ZeroMemory ( &stExpPtrs , sizeof ( EXCEPTION_POINTERS ) ) ; ZeroMemory ( &stExInfo ,sizeof(MINIDUMP_EXCEPTION_INFORMATION));

//Присвоение адресу исключения начала команды CALL.

//Интересно, что код исключения задавать не требуется.

//При открытии файла .DMP, созданного этим фрагментом

//программы, в VS.NET код исключения будет иметь вид:

//0x00000000: The operation completed successfully.

//Запрещение предупреждения C4312: 'type cast' : conversion

//from 'DWORD' к типу 'PVOID' of greater size (преобразование

//типа 'DWORD' к типу 'PVOID', имеющему больший размер). #pragma warning ( disable : 4312 ) stExRec.ExceptionAddress = (PVOID)(pCtx >Eip) ;

#pragma warning ( default : 4312 )

//Заполнение структуры stExpPtrs (типа EXCEPTION_POINTERS). stExpPtrs.ContextRecord = pCtx ;

stExpPtrs.ExceptionRecord = &stExRec ;

//Наконец я заполняю структуру информации об исключении. stExInfo.ThreadId = GetCurrentThreadId ( ) ; stExInfo.ClientPointers = TRUE ; stExInfo.ExceptionPointers = &stExpPtrs ;

//Создание файла для записи минидампа.

HANDLE hFile = CreateFile ( szDumpName

,

GENERIC_READ | GENERIC_WRITE ,

FILE_SHARE_READ

,

NULL

,

CREATE_ALWAYS

,

FILE_ATTRIBUTE_NORMAL

,

NULL

) ;

ASSERT ( INVALID_HANDLE_VALUE != hFile ) ;

 

if ( INVALID_HANDLE_VALUE != hFile )

{

// Запись файла минидампа.

ГЛАВА 13

Обработчики ошибок

511

 

 

 

 

 

 

 

 

 

BOOL bRetVal = g_pfnMDWD ( GetCurrentProcess ( )

,

 

GetCurrentProcessId ( ) ,

 

hFile

 

,

 

eType

 

,

 

&stExInfo

 

,

 

NULL

 

,

 

NULL

 

) ;

 

ASSERT ( TRUE == bRetVal ) ;

 

 

 

if ( TRUE == bRetVal )

 

 

 

{

 

 

 

eRet = eDUMP_SUCCEEDED ;

 

 

 

}

 

 

 

else

 

 

 

{

 

 

 

eRet = eMINIDUMPWRITEDUMP_FAILED ;

 

 

 

}

 

 

 

// Закрытие файла.

 

 

 

VERIFY ( CloseHandle ( hFile ) ) ;

 

 

 

}

 

 

 

else

 

 

 

{

 

 

 

eRet = eOPEN_DUMP_FAILED ;

 

 

 

}

 

 

 

}

 

 

 

return ( eRet ) ;

 

 

 

}

 

 

 

BSUMDRET __declspec ( naked )

 

 

 

SnapCurrentProcessMiniDumpW ( MINIDUMP_TYPE eType

,

 

LPCWSTR

szDumpName )

 

{

//Место хранения значений регистров,

//имевших место при вызове этой функции. CONTEXT stInitialCtx ;

//Место хранения заключительных значений регистров. CONTEXT stFinalCtx ;

//Возвращаемое значение.

BSUMDRET eRet ;

// Локальное возвращаемое значение типа Boolean.

BOOL bRetVal ;

// Выполнение пролога. SNAPPROLOG ( stInitialCtx ) ;

eRet = eDUMP_SUCCEEDED ;

// Проверка параметра строки.

ASSERT ( FALSE == IsBadStringPtr ( szDumpName , MAX_PATH ) ) ; if ( TRUE == IsBadStringPtr ( szDumpName , MAX_PATH ) )

см. след. стр.

512 ЧАСТЬ IV Мощные средства и методы отладки неуправляемого кода

{

eRet = eBAD_PARAM ;

}

 

if ( eDUMP_SUCCEEDED == eRet )

 

{

 

// Обнуление структуры заключительного контекста.

 

ZeroMemory ( &stFinalCtx , sizeof ( CONTEXT ) ) ;

 

// Я хочу получить все характеристики контекста.

 

stFinalCtx.ContextFlags = CONTEXT_FULL

|

CONTEXT_CONTROL

|

CONTEXT_DEBUG_REGISTERS

|

CONTEXT_EXTENDED_REGISTERS |

CONTEXT_FLOATING_POINT

;

// Получение значений всех регистров для контекста данного потока. bRetVal = GetThreadContext ( GetCurrentThread ( ) ,&stFinalCtx); ASSERT ( TRUE == bRetVal ) ;

if ( TRUE == bRetVal )

{

COPYKEYCONTEXTREGISTERS ( stFinalCtx , stInitialCtx ) ;

//Получение адреса возврата и адреса команды call,

//вызвавшей данную функцию. Всем остальным регистрам

//присвоены значения, которые они имели до вызова,

//поэтому указатель команд устанавливается аналогично. UINT_PTR dwRetAddr = (UINT_PTR)_ReturnAddress ( ) ;

bRetVal = CalculateBeginningOfCallInstruction ( dwRetAddr ); ASSERT ( TRUE == bRetVal ) ;

if ( TRUE == bRetVal )

{

//Установка указателя команд на начало команды call. stFinalCtx.Eip = (DWORD)dwRetAddr ;

//Вызов общей функции, выполняющей

//фактическую запись минидампа.

eRet = CommonSnapCurrentProcessMiniDump ( eType

,

szDumpName

,

&stFinalCtx );

}

else

{

eRet = eGETTHREADCONTEXT_FAILED ;

}

}

}

// Эпилог.

SNAPEPILOG ( eRet ) ;

}

Соседние файлы в предмете Программирование на C++