Отладка приложений

       

Основные моменты реализации


Сама программа CrashFinder является непосредственным MFC-приложением (т. е. напрямую использует библиотеку классов Microsoft Foundation Class), поэтому большая ее часть должна быть хорошо узнаваемой. Чтобы облегчить дальнейшее расширение программы CrashFinder (это можно сделать, следуя рекомендациям, приведенным в разделе "Что делать дальше с утилитой CrashFinder?" в конце этой главы), укажу на три ключевых момента и поясню основные особенности их реализации. Во-первых, речь пойдет о символьной машине, которую использует CrashFinder, во-вторых, мы рассмотрим, где выполняется основная работа в программе CrashFinder, и, наконец, опишем архитектуру данных этой программы.

CrashFinder использует символьную машину DBGHELP.DLL, описанную в главе 4. Единственной интересной деталью является то, что необходимо заставить эту машину загружать весь исходный файл и информацию о номерах строк, пересылая флажок SYMOPT_LOAD_LINES в функцию symsetoptions. Символьная машина DBGHELP.DLL по умолчанию не загружает исходный файл и информацию о номерах строк, так что об этом должен позаботиться программист.

Еще одна особенность реализации программы CrashFinder — вся работа, по существу, выполняется в документальном классе ccrashFinderDoc. Он содержит класс csyinboiEngine, осуществляет поиск всех символов и управляет их представлением (видом). Его ключевая функция— ccrashFinderDoc:: LoadAndShowimage — показана в листинге 8-2. Эта функция подтверждает правильность двоичного образа, проверяет его на наличие элементов проекта, имеющих конфликтующие адреса загрузки, загружает символы и вставляет образ в конец дерева. Она вызывается и при добавлении двоичного образа к проекту, и при открытии проекта. Перекладывая управление этими рутинными операциями на функцию CcrashFinderDoc: :LoadAndShowimage, разработчик может быть уверен, что основная логика программы CrashFinder всегда находится в одном месте, и что проект хранит только имена двоичных образов, а не копирует всю таблицу символов.


Листинг 8-2. Функция  CcrashFinderDoc: :LoadAndShowimage

BOOL CcrashFinderDoc :: LoadAndShowimage ( CBinaryImage * plmage,

BOOL bModifiesDoc)





// Проверить предположения за пределами функции.

ASSERT ( this);

ASSERT ( NULL != m_pcTreeControl);

// Строка, которая может использоваться для любых сообщений

CString sMsg ;

// Состояние для графики дерева

int iState = STATEJSIOTVALID;

// Переменная для булевского возвращаемого значения

BOOL   bRet ;

// Убедиться, что параметр — в порядке. 

ASSERT ( NULL != plmage);

 if ( NULL == plmage) 

{

// Ничего не может случиться с плохим указателем,

 return ( FALSE);

 }

// Проверить, правилен ли этот образ. Если это так, убедиться,

 // что его еще нет в списке и что он не имеет

 // конфликтующего адреса загрузки. Если это не так, все равно

 // добавить его, т. к. нехорошо просто выбрасывать данные

 // пользователя.

// Если образ плох, я просто показываю его с неправильным растром 

// и не загружаю в символьную машину,

 if ( TRUE == p!mage->IsValidImage ()) 

{

// Здесь сканируются элементы массива данных, чтобы отыскать

 // три проблемных условия:

// 1. Двоичный образ уже есть в списке. Если это так, то возможен

 // только преждевременный выход.

// 2. Двоичный образ будет загружен в адрес, который уже 

// есть в списке. Если это так, открываем диалоговое окно

 // Properties для двоичного образа, чтобы перед его 

// добавлением в список можно было изменить адрес загрузки. 

// 3. Проект уже включает исполняемый (ЕХЕ-)образ, и plmage тоже 

// является исполняемым.

// Для начала я всегда оптимистично предполагаю, что данные в

 // plmage правильны. 

BOOL bValid = TRUE;

int iCount = m_cDataArray.GetSize ();

 for ( int i = 0; i < iCount; i++) 

{

CBinaryImage * pTemp = (CBinaryImage *)m_cDataArray[ i ]; 

ASSERT ( NULL != pTemp); 

if ( NULL = pTemp) 



{

// Мало ли что может случиться с плохим указателем!

 return ( FALSE);

 }

// Согласованы ли два этих Cstring-значения?

 if ( pImage->GetFullName О = pTemp->GetFullName ())

 {

// Сообщить пользователю!!

sMsg.FormatMessage ( IDS_DUPLICATEFILE,

pTemp->GetFullName () ); 

AfxMessageBox ( sMsg);

return ( FALSE);

 }

// Если текущее изображение из структуры данных неправильно, 

// то проверим дублирование имен, как это было только что |

 // показано, но адреса загрузки и характеристики ЕХЕ-образа 

// проверить трудно. Если рТетр — неправильный, 

 //то следует пропустить эти проверки.. 

// Это может привести к проблемам, но т. к. рТетр отмечен

 //в списке как неправильный, то переустановка 

// свойств становится проблемой пользователя.

 if ( TRUE == pTemp.->IsValidIinage ( FALSE) ) 



// Проверить, что в проект не добавлены два ЕХЕ-файла.

if ( 0 == ( IMAGE_FILE_DLL &

pTemp->GetCharacteristics ())) 

{

if ( 0 = { IMAGE_FILE_DLL &

pImage->GetCharacteristics ())) 

{

// Сообщить пользователю!!

SMsg.FormatMessage ( IDS_EXEALREADYINPROJECT,

 p!mage->GetFullName (), pTemp->GetFullName () ); 

AfxMessageBox ( sMsg);

// Попытка загрузить два образа, помеченных как

 // "ЕХЕ", будет автоматически отбрасывать данные

 // для plmage. return ( FALSE);

 }



// Проверить конфликты адресов загрузки, 

if ( pImage->GetLoadAddress () == pTemp->GetLoadAddress() )

 {

sMsg.FormatMessage ( IDS_DUPLICATELOADADDR , 

pImage->GetFullName () , 

pTemp->GetFullName () );

if ( IDYES == AfxMessageBox ( sMsg, MB_YESNO)) 

{

// Пользователь хочет изменить свойства вручную

pImage~>SetProperties ();

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

// изменился и что нет конфликта

//с другим двоичным образом.

int iIndex;

if ( TRUE =

IsConflictingLoadAddress (



pImage->GetLoadAddress(),

 iIndex ) ) 

{

sMsg.FormatMessage

( IDS_DUPLICATELOADADDRFINAL,

p!mage->GetFullName () ,

((CBinaryImage*)m_cDataArray[iIndex])->GetFullName());

 AfxMessageBox ( sMsg);

// Данные в pImage неправильные, поэтому

 // двигаемся дальше и выходим из цикла. 

bValid = FALSE; 

break;

 }

}

else 

{

// Данные в plmage неправильные, поэтому

 // двигаемся дальше и выходим из цикла.

 bValid = FALSE; 

break; 







}

if ( TRUE = bValid) 

{

// Этот образ хорош (по крайней мере, по отношению

 //к загруженным символам).

 iState = STATE_VALIDATED;

 } 

else

{

iState = STATE_NOTVALID;

}

 }

else 

{

// Этот образ неправильный.

iState = STATE_NOTVALID;

 }

if ( STATE_VALIDATED = iState)

{

// Попытка загрузить этот образ в символьную машину.

bRet =

m_cSymEng.SymLoadModule(NULL ,

(PSTR)(LPCSTR)pImage->GetFullName(), 

 NULL

pImage->GetLoadAddress () , 

0 );

// Наблюдение закончено. SymLoadModule возвращает адрес загрузки

 // образа, неравный TRUE. 

ASSERT ( FALSE != bRet);

 if ( FALSE == bRet) 

{

TRACE ( "m_cSymEng.SymLoadModule failed!!\n"); 

iState = STATE_NOTVALID; 

}

else

 {

iState ь STATE_VALIDATED; 

}

 }

// Установить значение "Extra Data" для plmage в состояние загрузки

 // отладочных символов i

f ( STATEJVALIDATED == iState)

 {

pImage->SetExtraData ( TRUE); 

}

else 

{

pImage->SetExtraData ( FALSE);

 }

// Поместить этот элемент в массив.

 m_cDataArray.Add ( plmage);

// Добавлен ли элемент модификации документа?

 if ( TRUE == bModifiesDoc) 

{

SetModifiedFlag (); 

}

CCrashFinderApp * pApp = (CCrashFinderApp*)AfxGetApp (); 

ASSERT ( NULL != pApp);

// Поместить строку в дерево.

 HTREEITEM hltem =



m_pcTreeControl->Insert!tem ( pApp->ShowFullPaths ()

? pImage->GetFullName () 

: pImage->GetName () , 

iState ,

 iState );

ASSERT ( NULL != hltem);

// Поместить указатель на образ в данные элемента. Этот указатель 

// облегчает обновление символьной информации модуля 

// всякий раз, когда его вид претерпевает изменения.

 bRet = m_pcTreeControl->SetItemData ( hltem, (DWORD)plmage); 

ASSERT ( bRet);

// Форсировать выбор элемента.

 bRet = m_pcTreeControl->SelectItem ( hltem);

 // Bee OK, Jumpmaster!

 return ( bRet); 

}

И, наконец, опишем архитектуру данных программы CrashFinder. Ее главная структура данных — это просто массив классов cbinaryimage. Каждый класс cbinaryimage представляет отдельный двоичный образ, добавляемый к проекту, и обслуживает информацию о таких деталях этого образа, как адрес загрузки, двоичные свойства и имя. Документ1 добавляет объект cbinaryimage (двоичный образ) к массиву главных данных и помещает значение соответствующего указателя в слот данных узлового элемента дерева. При выборке элемента в представлении дерева двоичных файлов (в левой панели окна программы CrashFinder) выбранный узел пересылается назад в документ, так чтобы документ смог получить объект cbinaryimage и просмотреть его символьную информацию (предъявив ее пользователю в правой панели окна программы CrashFinder).

 Точнее — объект класса CcrashFinderDoc. — Пер.



Содержание раздела