

Программирование на языке MFC
Мой второй блог в серии программирования
Очевидно, что первое поле этой структуры представляет собой указатель на предыдущую структуру этого же типа, а второе поле -указатель на обработчик исключений. Из всего сказанного выше можно сделать вывод о том, что обработчики исключений связаны в однонаправленный список, указатель на начало которого записывается в структуру типа _NT_TIB, адрес которой, в свою очередь, записывается в регистр FS.
Итак, мы получили отправную точку для дальнейших поисков. Скорее всего, мы начинаем формировать в стеке структуру типа _EXCEPTION_REGISTRATION, в которую будет записан указатель на обработчик исключений, находящийся в нашей программе. Однако не забудьте о том, что стек растет от больших адресов памяти, поэтому сначала в стек будут записан указатель на обработчик исключения, а потом указатель на следующую структуру.
Теперь забежим немного вперед. В нашем потоке до сего момента не зарегистрировано НИ ОДНОГО обработчика исключений. Если мы сейчас взглянем на содержимое участка памяти, по которому расположен TIB (fs:[00000000]), то мы увидим, что значение первого двойного слова по этому адресу, т. е. значение слова, в ко-тором должен находится указатель на структуру типа _EXCEPTION_REGISTRATION, равно OxFFFFFFFF. Из этого вытекает, что значение OxFFFFFFFF является обозначением конца списка регистрации обработчиков исключений*
Чтобы проверить наш вывод, напишем попутно еще одну небольшую программу, которая будет читать указатель на начало списка обработчиков исключений, а потом выводить полученную информацию в окно отладки. Текст этой программы приведен ниже:
#include <afxwin.h> #include "tib.h"
// Объявляем класс приложения, его поля и методы, class CTheFirstApp : public CWinApp
{
public:
virtual BOOL Initlnstance();
};
// Инициализация класса приложения. BOOL CTheFirstApp::Initlnstance () {
PTIB pTIB ; asm
{
// Выбираем из TIB’а указатель на самого себя. MOV ЕАХ , FS:[ 18h ] MOV pTIB , ЕАХ
}
// Выдаем на отображение значение указателя на TIB. TRACE( "Pointer to the TIB = %x\n", pTIB );
// Перебираем весь список обработчиков исключений
for ( _ЕХСЕРТION_REGISTRATION_RECORD* pTempExceptionPointer
= pTIB->pvExcept; UINT( pTempExceptionPointer ) != Oxffffffff; pTempExceptionPointer = pTempExceptionPointer->pNext )
{
TRACE("Pointer to
_EXCEPTION_REGISTRATION_RECORD=%08x\n",
pTempExceptionPointer ); TRACE( "Pointer to the next record = %08x\n",
pTempExceptionPointer->pNext ); TRACE( "Pointer to the exception handler = %08x\n",
pTempExceptionPointer->pfnHandler );
}
return TRUE;
CTheFirstApp theApp;
читать отзывы (0)
Ниже приведен файл tib.h:
//==============================================
// File: TIB.H
// Author: Matt Pietrek
// From: Microsoft Systems Journal "Under the Hood", // May 1996
//===============================================
#pragma pack(l)
typedef struct _EXCEРТION_REGISTRATION_RECORD {
struct _EXCEРТION_REGISTRATION_RECORD * pNext; FARPROC pfnHandler; } EXCEPTION_REGISTRATION_RECORD,
*РЕХСЕ РТION_REGISTRATION_RECORD; typedef struct _TIB {
PEXCEPTION_REGISTRATION_RECORD pvExcept; // OOh Head of
// exception record list
PVOID pvStackUserTop; // 04h Top of user stack
PVOID pvStackUserBase; // 08h Base of user stack
union // OCh (NT/Win95 differences)
{
struct // Win95 fields {
WORD pvTDB; // OCh TDB
WORD pvThunkSS; // OEh SS selector used
// for thunking to 16 bits
DWORD unknownl; // lOh
} WIN95;
struct // WinNT fields {
PVOID SubSystemTib; // OCh
ULONG FiberData; // lOh
} WINNT; } TIBJJNION1;
PVOID pvArbitrary; // 14h Available for application use struct _tib *ptibSelf; // 18h Linear address of TIB structure
union {
// ICh (NT/Win95 differences)
struct // Win95 fields {
WORD WORD DWORD DWORD DWORD } WIN95;
ICh lEh 20h 24h
// // // //
TIBFlags; Winl6MutexCount; DebugContext; pCurrentPriority;
// 28h Message Queue selector
struct // WinNT fields {
DWORD unknownl;
// IChDWORD processID; // 20h
DWORD threadID; // 24h
DWORD unknown2; // 28h
} WINNT; } TIB_UNION2;
PVOID* pvTLSArray; // 2Ch Thread Local Storage array
union // 30h (NT/WinJ5 differences)
{
struct // Win95 fields {
PVOID* pProcess; // 30h Pointer to owning // process" database
} WIN95; } TIB_UNI0N3;
} TIB, *PTIB; #pragma pack()
И наконец, результат работы программы:
Pointer to the TIB = 815f75cc
Pointer to _EXCEРТION_REGISTRATION_RECORD = 0064fe28
Pointer to the next record = 0064ЈЈ68
Pointer to the exception handler = 00401c38
Pointer to _EXCEРТION_REGISTRATION_RECORD = 0064ff68
Pointer to the next record = ffffffff
Pointer to the exception handler = bffc05b4
Warning: m_pMainWnd is NULL in CWinApp: :Run – quitting
application.
Объем выдаваемой в структуре типа CONTEXT информации регулируется полем ContextFlags. Назначение флагов этого поля приведено в табл. 6.
Необходимо заметить, что структуры, о которых говорилось выше, доступны ТОЛЬКО в фильтре исключений. Дело в том, что при передаче исключения обработчику данные в стеке разрушаются. Следовательно, для того чтобы иметь возможность каким-то образом проанализировать информацию об исключении, мы должны где-то сохранить данные, сформированные при исключении, верно? Чтобы продемонстрировать, каким образом можно это сделать, я написал небольшую программу. Эта программа сохраняет сформированные в момент исключения данные и затем отображает их в окне отладки. Исходный текст программы приведен ниже:
tinclude <afxwin.h>
// Объявляем класс приложения, его поля и методы.
class CTheFirstApp : public CWinApp
{
public:
virtual BOOL Initlnstance();
};
// Инициализация класса приложения. BOOL CTheFirstApp::Initlnstance() {
EXCEPTION_RECORD erMyExceptionRecord; CONTEXT cMyContext;
__ try
{
RaiseException ( 0xc0000005, EXCEPTION_NONCONTINUABLE, 0, NULL );
_ except( memcpy( SerMyExceptionRecord,
(GetExceptionlnformation())->ExceptionRecord, sizeof( EXCEPTION_RECORD ) ), memcpy( &cMyContext,
(GetExceptionlnformation ())->ContextRecord, sizeof ( CONTEXT ) ), EXCEPTION_EXECUTE_HANDLER )
{
TRACE( "erMyExceptionRecord.ExceptfonCode = %08x\n \ erMyExceptionRecord.ExceptionFlags = %08x\n \ erMyExceptionRecord.ExceptionRecord = %08x\n \ erMyExceptionRecord.ExceptionAddress = %08x\n \ erMyExceptionRecord.NumberParameters = %08x\n", erMyExceptionRecord.ExceptionCode, erMyExceptionRecord.ExceptionFlags, erMyExceptionRecord.ExceptionRecord, erMyExceptionRecord.ExceptionAddress, erMyExceptionRecord.NumberParameters ) ;
if( cMyContext.ContextFlags & CONTEXT_CONTROL ) TRACE( "cMyContext.Ebp = %08x\n \
cMyContext.Eip = %08x\n \
cMyContext.SegCs = %08x\n \
cMyContext.EFlags = %08x\n \
cMyContext.Esp = = %08x\n \
cMyContext.SegSs = %08x\n",
cMyContext.Ebp,
cMyContext.Eip,
cMyContext.SegCs,
cMyContext.EFlags,
cMyContext.Esp,
cMyContext.SegSs ); if( cMyContext.ContextFlags & CONTEXT_INTEGER ) TRACE( "cMyContext.Edi = %08x\n \
CMyContext.Esi = %08x\n \
cMyContext.Ebx = %08x\n \
CMyContext.Edx = %08x\n \
cMyContext.Ecx = %08x\n \
cMyContext.Eax = %08x\n",
cMyContext.Edi,
cMyContext.Esi,
cMyContext.Ebx,
cMyContext.Edx,
cMyContext.Есх,
cMyContext.Еах ); if( cMyContext.ContextFlags & CONTEXTJSEGMENTS ) TRACE( "cMyContext.SegGs = %08x\n \
cMyContext.SegFs = %08x\n \
cMyContext.SegEs = %08x\n \
cMyContext .SegDs = %08x\n",
cMyContext.SegGs,
cMyContext.SegFs,
cMyContext.SegEs,
cMyContext.SegDs )/ if( cMyContext.ContextFlags & CONTEXT_FLOATING_POINT ) TRACE ( "cMyContext.FloatSave.ControlWord = %08x\n \
cMyContext.FloatSave.StatusWord = %08x\n \
cMyContext.FloatSave.TagWord = %08x\n \
cMyContext.FloatSave.ErrorOffset = %08x\n \ cMyContext.FloatSave.ErrorSelector = %08x\n \
cMyContext.FloatSave.DataOffset = %08x\n \ cMyContext.FloatSave.DataSelector = %08x\n",
cMyContext.FloatSave.ControlWord,
cMyContext.FloatSave.StatusWord,
cMyContext.FloatSave.TagWord,
cMyContext.FloatSave.ErrorOffset,
cMyContext.FloatSave.ErrorSelector,
cMyContext.FloatSave.DataOffset,
cMyContext.FloatSave.DataSelector ); if( cMyContext.ContextFlags & CONTEXT_DEBUG_REGISTERS ) TRACE( "cMyContext.DrO = %08x\n \
cMyContext.Drl = %08x\n \
cMyContext.Dr2 = %08x\n \
cMyContext.Dr3 = %08x\n \
cMyContext.Dr6 = %08x\n \
cMyContext.Dr7 = %08x\n",
cMyContext.DrO,
cMyContext.Dr1,
cMyContext.Dr2,
cMyContext.Dr3,
cMyContext.Dr6,
cMyContext.Dr7 );
}
return TRUE;
}
CTheFirstApp theApp;
Результат работы этой программы приведен ниже:
erMyExceptionRecord.ExceptionCode = сО000005
erMyExceptionRecord.ExceptionFlags = 00000001 erMyExceptionRecord.ExceptionRecord = 00000000 erMyExceptionRecord.ExceptionAddress = 0040109a erMyExceptionRecord.NumberParameters = 00000000 cMyContext.Ebp = 0064fd60
cMyContext.Eip = 0040109a *• cMyContext.SegCs = 00000167 cMyContext.EFlags = 00000207 cMyContext.Esp = = 0064f9dc cMyContext.SegSs = 0000016f cMyContext.Edi = 0064fd48
cMyContext.Esi = 0064f9dc cMyContext.Ebx = 00540000 cMyContext.Edx = 004135d4 cMyContext.Ecx = 00414748 cMyContext.Eax = cccccccc cMyContext.SegGs = 00002a2e
cMyContext.SegFs = 00002167 cMyContext.SegEs = 0000016f cMyContext.SegDs = 0000016f cMyContext.FloatSave.ControlWord = ffff027f
cMyContext.FloatSave.StatusWord = ffffOOOO cMyContext.FloatSave.TagWord = ffffffff cMyContext.FloatSave.ErrorOffset = 00000000 cMyContext.FloatSave.ErrorSelector = 00000000 cMyContext.FloatSave.DataOffset = 00000000 cMyContext.FloatSave.DataSelector = ffffOOOO cMyContext.DrO = 00000000
cMyContext.Drl = 00000000 cMyContext.Dr2 = 00000000 cMyContext.Dr3 = 00000000 cMyContext.Dr6 = 00000000 cMyContext.Dr7 = 00000000 Warning: m_j?MainWnd is NULL in CWinApp: :Run – quitting application.
Я подробно рассказал о механизме обработки исключений средствами Windows. Теперь настало время рассказать о том, как решается проблема обработки исключений в MFC.
Наверное, нужно обратить внимание и на то, что в пределах одной функции допускается использование только одной формы обработки исключений. Поэтому внутри функции, использующей С++-форму обработки исключений нам необходимо будет вызвать функцию, использующую SEH-форму обработки исключений. Исходя из всего сказанного выше, мной была написана небольшая программа, которая демонстрирует, каким образом информация о прерываниях SEH может стать доступной MFC:
#include <afxwin.h> #include <eh.h>
void SehToMFCTranslator( UINT, EXCEPTION_POINTERS* ); void RaiseExceptionCall( void );
class CSehToMFCException : public CException { public:
// Структура для хранения данных из стека
EXCEPTION_RECORD erMyExceptionRecord; // Структура для хранения данных из стека
CONTEXT cMyContext’;
CSehToMFCException(
EXCEPTION_POINTERS* epExceptionPointers ) ;
};
CSehToMFCException::CSehToMFCException(
EXCEPTION_POINTERS* epExceptionPointers )
{
memcpy( SerMyExceptionRecord,
epExceptionPointers->ExceptionRecord,
sizeof( EXCEPTION_RECORD ) ); memcpy( &cMyContext,
epExceptionPointers->ContextRecord,
sizeof ( CONTEXT ) );
}
class CSehToMFCApp : public CWinApp {
public:
BOOL Initlnstance ();
};
BOOL CSehToMFCApp::Initlnstance() {
try {
_set_se_translator( SehToMFCTranslator ); RaiseExceptionCall ();
}
catch ( CSehToMFCException* pException ) {
TRACE( "In catch block.\n" );
TRACE( "erMyExceptionRecord.ExceptionCode = %08x\n \ erMyExceptionRecord.ExceptionFlags = %08x\n \ erMyExceptionRecord.Exception"Record = %08x\n \ erMyExceptionRecord.ExceptionAddress = %08x\n \ erMyExceptionRecord.NumberParameters = %08x\n", pExcept ion->erMyExceptionRecord.ExceptionCode, pException->erMyExceptionRecord.ExceptionFlags,
pException->erMyExceptionRecord.ExceptionRecord, pException->erMyExceptionRecord.ExceptionAddress, pException->erMyExceptionRecord.NumberParameters ) ; if ( pException->cMyContext.ContextFlags & CONTEXT_CONTROL ) TRACE ( "cMyContext.Ebp = %08x\n \ cMyContext.Eip = %08x\n \ cMyContext.SegCs = %08x\n \ cMyContext.EFlags = %08x\n \ cMyContext.Esp = = %08x\n \ cMyContext.SegSs = %08x\n", pException->cMyContext.Ebp, pException->cMyContext.Eip, pException->cMyContext.SegCs, pException->cMyContext.EFlags, pException->cMyContext.Esp, pException->cMyContext.SegSs ); if( pException->cMyContext.ContextFlags & CONTEXT_INTEGER ) TRACE( "cMyContext.Edi = %08x\n \ cMyContext.Esi = %08x\n \ cMyContext.Ebx = %08x\n \ cMyContext.Edx = %08x\n \ cMyContext.Ecx = %08x\n \ cMyContext.Eax = %08x\n", pException->cMyContext.Edi, pException->cMyContext.Esi, pException->cMyContext.Ebx, pException->cMyContext.Edx, pException->cMyContext.Ecx, pException->cMyContext.Eax ); if( pException->cMyContext.ContextFlags & CONTEXT_SEGMENTS ) TRACE( "cMyCSntext.SegGs = %08x\n \ cMyContext.SegFs = %08x\n \ cMyContext.SegEs = %08x\n \ cMyContext.SegDs = %08x\n", pException->cMyContext.SegGs, pException->cMyContext.SegFs, pException->cMyContext.SegEs, pException->cMyContext.SegDs ); if( pException->cMyContext.ContextFlags & CONTEXT_FLOATING_POINT )
TRACE( "cMyContext.FloatSave.ControlWord = %08x\n \ cMyContext.FloatSave.StatusWord = %08x\n \ cMyContext.FloatSave.TagWord = %08x\n \ cMyContext.FloatSave.ErrorOffset = %08x\n \ cMyContext.FloatSave.ErrorSeiector = %08x\n \ cMyContext.FloatSave.DaTaOffset = %08x\n \ cMyContext.FloatSave.DataSelector = %08x\n", pException->cMyContext.FloatSave.ControlWord, pException->cMyContext.FloatSave.StatusWord, pException->cMyContext.FloatSave.TagWord, pException->cMyContext.FloatSave.ErrorOffset, pException->cMyContext.FloatSave.ErrorSelector, pException->cMyContext.FloatSave.DataOffset, pException->cMyContext.FloatSave.DataSelector
) ;
if( pException->cMyContext.ContextFlags & CONTEXT_DEBUG_REGISTERS ) TRACE( "cMyContext.DrO = %08x\n \
cMyContext.Drl = %08x\n \
cMyContext.Dr2 = %08x\n \
cMyContext.Dr3 = %08x\n \
cMyContext.Dr6 = %08x\n \
cMyContext.Dr7 = %08x\n", ‘ pException->cMyContext.DrO,
pException->cMyContext.Drl,
pException->cMyContext.Dr2,
pException->cMyContext.Dr3,
pException->cMyContext.Dr6,
pException->cMyContext.Dr7 ); pException->Delete(); return TRUE;
}
return TRUE;
}
void SehToMFCTranslator( UINT nExceptionCode,
EXCEPTION_POINTERS* epExceptionPointers )
{
TRACE( "In SehToMFCTranslator().\n" );
throw ( new CSehToMFCException( epExceptionPointers ) ) ;
}
void RaiseExceptionCall ()
{
__ try
{
TRACE( "Before RaiseException () .\n" ); RaiseException( 0xC0000005,
EXCEPTION_NONCONTINUABLE, 0, NULL )/
}
__ finally
{
TRACE( "In finally block.\n" );
}
}
CSehToMFCApp theApp;
Результат работы этой программы:
Before RaiseException(). In SehToMFCTranslator().
In finally block.
In catch block.
erMyExceptionRecord. ExceptionCode = c0000005
erMyExceptionRecord.ExceptionFlags = 00000001 erMyExceptionRecord.ExceptionRecord = 00000000 erMyExceptionRecord. ExceptionAddress = 00401639 erMyExceptionRecord. Number Parameters = 00000000 cMyContext.Ebp = 0064fcf0
cMyContext.Eip = 00401639
cMyContext.SegCs = 00000167
cMyContext.EFlags = 00000202
cMyContext.Esp = = 0064fc8c
cMyContext.SegSs = 0000016f cMyContext.Edi = 0064fcd8
cMyContext.Esi = 0064fc8c
cMyCfon text. Ebx = 00540000
cMyContext.Edx = 00000000
cMyContext.Ecx = 00000000
cMyContext.Eax = 5f4ce0f0 cMyContext.SegGs = 000031e6
cMyContext.SegFs = 00003b67
cMyContext.SegEs = 0000016f
cMyContext.SegDs = 0000016f cMyContext.FloatSave.ControlWord = ffff027f
cMyContext.FloatSave.StatusWord = ffffOOOO
cMyContext.FloatSave.TagWord = ffffffff
cMyContext.FloatSave.ErrorOffset = 00000000 cMyContext.FloatSave.ErrorSelector = 00000000
cMyContext.FloatSave.DataOffset = 00000000
cMyContext.FloatSave.DataSelector = ffffOOOO cMyContext.DrO = 00000000
cMyContext.Drl = 00000000
cMyContext.Dr2 = 00000000
cMyContext.Dr3 = 00000000
cMyContext.Dr6 = 00000000
cMyContext.Dr7 = 00000000 Warning: mjpMainWnd is NULL in CWinApp::Run – quitting application.
Ура! Мы сделали это! Как видно из результатов работы программы, результаты обработки исключения средствами Windows стали доступны в MFC.
Мне думается, что об исключениях можно рассказывать еще достаточно долго. И вопросов осталось еще достаточно много. Но, думаю, вы знаете уже достаточно много об обработке исключений, чтобы самостоятельно находить ответы на возникающие вопросы.
