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

Рихтер Дж., Назар К. - Windows via C C++. Программирование на языке Visual C++ - 2009

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

858Часть V. Структурная обработка исключений

}else { // все данные сохранены

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

//вызывав RegisterApplicationRestart (если известно имя файла

//для восстановления).

//уведомляем о завершении восстановления

ApplicationRecoveryFinished(TRUE);

//закончив, устанавливаем bCancelled в TRUE, чтобы выйти из цикла bCancelled = TRUE;

}

}

}

return(dwReturn);

}

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

Оглавление

 

П Р И Л О Ж Е Н И Е А Среда разработки....................................................................................

860

Заголовочный файл CmnHdr.h .............................................................................................

860

Раздел Windows Version Build Option..........................................................................

861

Раздел Unicode Build Option...........................................................................................

861

Раздел Windows Definitions и диагностика уровня 4 ..............................................

862

Вспомогательный макрос Pragma Message ..............................................................

862

Макрос chlNRANGE ...........................................................................................................

863

Макрос chBEGINTHREADEX............................................................................................

863

Моя реализация DebugBreak для платформы х86 ..................................................

865

Определение кодов программных исключений ......................................................

865

Макрос chMB .......................................................................................................................

865

Макросы chASSERT и chVERIFY ...................................................................................

865

Макрос chHANDLE_DLGMSG ..........................................................................................

866

Макрос chSETDLGICONS .................................................................................................

866

Принудительное указание компоновщику входной функции (w)WinMain.......

866

Поддержка тем оформления Windows ХР с помощью директивы pragma .....

867

П Р И Л О Ж Е Н И Е А

Среда разработки

При сборке программ-примеров вам придется иметь дело с ключами компилятора и компоновщика. Чтобы не засорять ими тексты программ, я выделил их почти все в один заголовочный файл CmnHdr.h, включаемый в исходные файлы.

К сожалению, некоторые параметры разместить там нельзя, и поэтому пришлось вносить кое-какие изменения в свойства проекта каждой программы. Во всех проектах я открывал диалоговое окно Properties и изменял следующие на-

стройки Configuration Properties:

на вкладке General в поле Output Directory я указывал конкретный каталог, чтобы в него помещались все ЕХЕ- и DLL-файлы;

на вкладке C/C++, Code Generation в списке Run-Time Library я выбирал MultiThreaded DLL;

на вкладке C/C++ в списке Detect 64-Bit Portability я устанавливал значение Yes (/Wp64).

Вот и все. Лишь эти три параметра требовали модификации вручную, для ос-

тальных параметров меня устраивали значения, предлагаемые по умолчанию. Кстати, эти изменения я вносил как в Debug-, так и в Release-версию каждого проекта (т. е. в его отладочную и конечную версию). Остальные настройки компилятора и компоновщика мне удалось разместить в исходном коде, и они вступают в силу, как только вы включаете в свой проект любой из моих модулей исходного кода.

Заголовочный файл CmnHdr.h

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

Приложение А. Среда разработки.docx 861

Далее я расскажу обо всех разделах заголовочного файла CmnHdr.li и объясню, для чего предназначен каждый из них, а также как его изменить и зачем.

Раздел Windows Version Build Option

Поскольку часть моих программ обращается к новым функциям, появившимся только в Windows Vista, в этом разделе определяются идентификаторы

_WIN32_WINNT и WINVER:

// = 0x0600 for VISTA level from sdkddkver.h #define _WIN32_WINNT _WIN32_WINNT_L0NGH0RN #deflne WINVER _WIN32_WINNT_L0NGH0RN

Мне пришлось это сделать, так как прототипы новых функций в заголовочных файлах Windows задаются так:

#if (_WIN_WINNT >= 0x0600)

HANDLE

WINAPI CreateMutexExW(

LPSECURITY_ATTRIBUTES lpMutexAttributes, LPCWSTR lpName,

DWORD dwFlags,

DWORD dwDesiredAccess );

#endif /* _WIN32_WINNT >= 0x0600 */

Пока вы сами не определите _WIN32_WINNT (до включения файла Windows.h), прототипы новых функций не будут объявлены, и компилятор, встретив попытки вызова этих функций, сообщит об ошибках. Майкрософт специально защитила эти функции идентификатором _WIN32_WINNT, чтобы разработчики, ориентирующиеся на разные версии Windows, случайно не использовали функции, существующие пока только в Windows Vista.

Раздел Unicode Build Option

Если вы хотите создать Unicode-версию программы для процессоров х86, раскомментируйте всего одну строку, в которой определяется макрос UNICODE, и перекомпилируйте программу. Определяя макрос UNICODE в файле CmnHdr.h, я упрощаю себе управление сборкой программ-примеров. Подробнее о Unicode см. главу 2.

862 Часть VI. Приложения

Раздел Windows Definitions и диагностика уровня 4

Разрабатывая программы, я всегда стараюсь, чтобы компилятор мог транслировать их, не выдавая предупреждений и сообщений об ошибках. Кроме того, я предпочитаю устанавливать при компиляции наивысший уровень диагностики. В этом случае компилятор делает за меня максимум работы и проверяет в коде даже самые незначительные мелочи. Для компиляторов Microsoft C/C++ это означает, что я компилировал все примеры с использованием диагностики уровня 4.

Увы, отдел операционных систем в Майкрософт не разделяет моих сантиментов насчет компиляции с применением диагностики уровня 4. В итоге, когда я установил такой уровень, компилятор выдал предупреждения по множеству строк из заголовочных файлов самой Windows. К счастью, они не свидетельствуют о каких-то проблемах в коде — большинство из них генерируется из-за нетипичного употребления конструкций языка С, в которых используются расширения, реализованные практически всеми поставщиками Windows-совместимых компиляторов.

Поэтому в данном разделе CmnHdr.h я указываю уровень диагностики 3, включаю стандартный заголовочный файл Windows.h, а потом задаю уровень диагностики 4. На этом уровне компилятор часто выдает предупреждения по таким ерундовым поводам, что приходится вставлять директиву #pragma warning для подавления некоторых предупреждений.

Вспомогательный макрос Pragma Message

Мне всегда хочется, чтобы какая-то часть программы, которую я еще пишу, сразу же начала работать, а доводку обычно откладываю на потом. Раньше, когда мне надо было напомнить себе, дескать, этот участок кода еще потребует внимания, я включал в код строки типа:

#pragma message("Fix this later")

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

А для этого не обойтись без набора макросов. В итоге я создал макрос chMSG, вызываемый так:

#pragma chMSG(Fix this later)

Дойдя до соответствующей строки, компилятор выдает что-то вроде:

C:\CD\CominonFiles\CmnHdr.h(82): Fix this later

Приложение А. Среда разработки.docx 863

Теперь, работая в Microsoft Visual Studio, можно дважды щелкнуть это сообщение в окне вывода, и тогда открывается соответствующий файл, а курсор ввода переходит на нужную строку.

И еще одна удобная мелочь — макрос chMSG не требует заключать текстовую строку в кавычки.

Макрос chlNRANGE

Это весьма полезный макрос, которым я часто пользуюсь в своих программах. Он проверяет, укладывается ли какое-то значение в заданный диапазон.

Макрос chBEGINTHREADEX

Все многопоточные программы из этой книги используют вместо Windowsфункции CreateThread функцию _beginthreadex из библиотеки Microsoft С/ С++. Это связано с тем, что _beginthreadex подготавливает новый поток так, чтобы он мог вызывать библиотечные функции, а при его завершении гарантирует уничтожение информации, используемой библиотекой в данном потоке (подробнее см. главу 6). К сожалению, прототип этой функции имеет вид:

unsigned long __cdecl _beginthreadex( void *,

unsigned,

unsigned (__stdcall *)(void *), void *,

unsigned, unsigned *);

Хотя значения параметров _beginthreadex идентичны значениям параметров функции CreateThread, их типы не совпадают. Вот прототип CreateThread:

typedef DWORD (WINAPI *PTHREAD_START_ROUTINE)(PVOID pvParam);

HANDLE CreateThread(

PSECURITY_ATTRIBUTES psa,

SIZE_T cbStackSize,

PTHREAD_START_ROUTINE pfnStartAddr,

PVOID pvParam,

DWORD dwCreateFlags,

PDWORD pdwThreadId);

Типы данных Windows-функции и ее библиотечного аналога так отличаются потому, что группа разработчиков библиотеки Microsoft C/C++ не пожелала зависеть от группы разработчиков операционных систем. Похвальное решение, но оно затруднило использование функции _beginthreadex.

864Часть VI. Приложения

Втом, как Майкрософт объявила прототип _beginthreadex, есть целых две проблемы. Во-первых, некоторые типы данных, используемые этой функцией, не совпадают с базовыми типами, примененными в CreateThread. Например, в Windows API тип данных DWORD определен как:

typedef unsigned long DWORD;

К этому типу данных относятся параметры cbStack и fdwCreate функции CreateThread. Но в прототипе _begjtnthreadex эти параметры объявлены как unsigned, что на деле означает unsigned int. Компилятор считает типы unsigned int и unsigned long разными и выдает предупреждение. Поскольку _beginthreadex не является частью стандартной библиотеки C/C++ и существует лишь как альтернатива Windows-функции CreateThread, я полагаю, что Майкрософт следовало бы объявить прототип _beginthreadex так, чтобы предупреждения не генерировались:

unsigned long __cdecl _beginthreadex( void *psa,

unsigned long cbStackSize,

unsigned (__stdcall *) (void *pvParam), void *pvParam,

unsigned long dwCreateFlags, unsigned long *pdwThreadId);

Вторая проблема — продолжение первой. Функция _beginthreadex возвращает значение типа unsigned long — описатель только что созданного потока. Обычно программа сохраняет это значение в переменной типа HANDLE:

HANDLE hThread = _beginthreadex(…);

Эта строка заставит компилятор выдать другое предупреждение. Чтобы избежать этого предупреждения, надо переписать строку с явным преобразованием типов:

HANDLE hThread = (HANDLE) _beginthreadex(…);

Это, конечно, неудобно. Чтобы компилятор ко мне не приставал, я определил в файле CmnHdr.h макрос chBEGINTHREADEX, который сам делает эти преобразования:

typedef unsigned (_stdcall *PTHREAD_START) (void *);

 

#define chBEGINTHREADEX(psa, cbStackSize, pfnStartAddr,

\

pvParam, dwCreateFlags, pdwThreadId)

\

((HANDLE)_beginthreadex(

\

(void *)

(psa),

\

(unsigned)

(cbStackSize),

\

(PTHREAD_START)

(pfnStartAddr),

\

(void *)

(pvParam),

\

 

 

Приложение А. Среда разработки.docx 865

(unsigned)

(dwCreateFlags),

\

(unsigned *)

(pdwThreadld)))

 

Моя реализация DebugBreak для платформы х86

Иногда мне нужно прервать код в какой-то точке, даже если процесс не выполняется под управлением отладчика. Для этого поток должен вызвать функцию DebugBreak, которая содержится в Kernel32.dll. Она позволяет подключить к процессу отладчик. Как только отладчик подключается к процессу, регистр указателя команд позиционируется на машинную команду, вызвавшую прерывание (останов). Эта команда находится в функции DebugBreak из Kernel32.dll, и, чтобы увидеть свой исходный код, я должен выйти из DebugBreak, используя режим пошагового выполнения кода.

На процессорной платформе х86 выполнение кода прерывается инструкцией «int 3». Поэтому я переопределил DebugBreak для платформы я86, включив эту ассемблерную инструкцию и сделав функцию подставляемой в код. При выполнении моей DebugBreak перехода в Kernel32.dll не происходит, прерывание возникает непосредственно в моем коде, и регистр указателя команд позиционируется на следующий оператор языка C/C++. Так удобнее.

Определение кодов программных исключений

Работая с программными исключениями, вы должны определить собственные 32битные коды исключений. Эти коды имеют специфический формат, о котором я рассказывал в главе 24. Чтобы упростить определение кодов программных исключений, я использую макрос MAKESOFTWAREEXCEPTTON.

Макрос chMB

Он просто выводит окно, в заголовке которого показывает полное (вместе с путем) имя исполняемого файла вызывающего процесса.

Макросы chASSERT и chVERIFY

Чтобы упростить выявление ошибок при разработке программ, я часто вкраплял в код макросы chASSERT. Этот макрос проверяет, истинно ли выражение х, и, если нет, выводит окно с именем файла, строкой и выражением, вычисление которого закончилось неудачно. В готовой программе макрос chASSERT раскрывается в пустое определение. Макрос chVERIFY практически идентичен предыдущему за исключением того, что выражения проверяются не только при отладке, но и в готовой программе.

866 Часть VI. Приложения

Макрос chHANDLE_DLGMSG

Используя распаковщики сообщений (message crackers) при работе с диалоговыми окнами, нельзя обращаться к макросу HANDLE_MSG из заголовочного файла WindowsX.h. Дело в том, что он не возвращает TRUE или FALSE в зависимости от того, обработано сообщение процедурой диалогового окна или нет. Чтобы устранить эту проблему, я и написал макрос chHANDLE_DLGMSG.

Макрос chSETDLGICONS

Поскольку во многих программах-примерах диалоговое окно выступает как главное, его значок надо изменять вручную, чтобы он корректно показывался и на панели задач, и в окне переключения задач, и в заголовке самого окна программы. Макрос chSETDLGICONS, вызываемый всякий раз, когда диалоговое окно получает сообщение WM INITDIALOG, обеспечивает корректную обработку значков.

Принудительное указание компоновщику входной функции (w)WinMain

Некоторые читатели предыдущих изданий этой книги, включавшие мои модули исходного кода в новые проекты, получали при их сборке сообщения об ошибках от компоновщика. Проблема была в том, что они создавали проект Win32 Console Application, заставляя компоновщик искать входную функцию (w)main. Но, поскольку все мои программы-примеры являются GUI-приложениями, в их исходном коде используется входная функция _tWinMain — вот почему компоновщик сообщал об ошибках.

Мой стандартный ответ этим читателям был таков: удалите текущий проект, создайте в проект Win32 Application (в названии шаблона проектов этого типа не должно быть слова «Console») и добавьте в него мои файлы с исходным кодом. Тогда компоновщик будет искать входную функцию (w)WinMain, которая и присутствует в моем коде, — никаких ошибок при сборке не возникнет.

В конце концов, чтобы сократить поток почты по этому поводу, я добавил в файл CmnHdr.h директиву pragma, которая заставляет компоновщик искать входную функцию (w)WinMain, даже если вы создали в Visual Studio проект Win32 Console Application.

Чем отличаются эти типы проектов Visual С++, как компоновщик выбирает нужный вид входных функций и как изменить стандартное поведение компоновщика, я подробно объяснил в главе 4.

Приложение А. Среда разработки.docx 867

Поддержка тем оформления Windows ХР с помощью директивы pragma

Windows, начиная с Windows ХР, поддерживает наборы настроек внешнего вида — темы оформления, определяющие облик большинства элементов пользовательского интерфейса системы и приложений. Однако по умолчанию приложения не поддерживают темы. Простой способ наделить приложение поддержкой тем — добавить к нему ХМL-манифест, требующий связывания к корректной версии библиотеки ComCtl32.dll, которая придаст элементам интерфейса Windows правильный вид. Компоновщик Microsoft С++ поддерживает ключ manifestdependency, который задают в CmnHdr.h, используя директиву pragma с соответствующими параметрами (подробнее о поддержке тем оформления см. в статье «Using Windows ХР Visual Styles» по ссылке (http://msdn2.microsoft.com/enus/library/ms997646.aspx).

CmnHdr. H

/****************************************************************************** Module: CmnHdr.h

Notices: Copyright (c) 2008 Jeffrey Richter & Christophe Nasarre Purpose: Common header file containing handy macros and definitions

used throughout all the applications in the book. See Appendix A.

******************************************************************************/

#pragma once // Include this header file once per compilation unit

//////////////////////// Windows Version Build Option /////////////////////////

// = 0x0600 for VISTA level from sdkddkver.h #define _WIN32_WINNT _WIN32_WINNT_LONGHORN #define WINVER _WIN32_WINNT_LONGHORN

//////////////////////////// Unicode Build Option /////////////////////////////

// Always compiler using Unicode. #ifndef UNICODE

#define UNICODE #endif

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