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

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

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

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

//When using Unicode Windows functions, use Unicode C-Runtime functions too.

#ifdef UNICODE #ifndef _UNICODE

#define _UNICODE #endif

#endif

///////////////////////// Include Windows Definitions /////////////////////////

#pragma warning(push, 3)

 

#include <Windows.h>

 

#pragma warning(pop)

 

#pragma warning(push, 4)

 

#include <CommCtrl.h>

 

#include <process.h>

// For _beginthreadex

///////////// Verify that the proper header files are being used //////////////

#ifndef FILE_SKIP_COMPLETION_PORT_ON_SUCCESS

#pragma message("You are not using the latest Platform SDK header/library ") #pragma message("files. This may prevent the project from building correctly.") #endif

////////////// Allow code to compile cleanly at warning level 4 ///////////////

/* nonstandard extension 'single line comment' was used */ #pragma warning(disable:4001)

//unreferenced formal parameter #pragma warning(disable:4100)

//Note: Creating precompiled header #pragma warning(disable:4699)

//function not inlined #pragma warning(disable:4710)

//unreferenced inline function has been removed #pragma warning(disable:4514)

//assignment operator could not be generated #pragma warning(disable:4512)

//conversion from 'LONGLONG' to 'ULONGLONG', signed/unsigned mismatch #pragma warning(disable:4245)

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

//'type cast' : conversion from 'LONG' to 'HINSTANCE' of greater size

#pragma warning(disable:4312)

//'argument' : conversion from 'LPARAM' to 'LONG', possible loss of data #pragma warning(disable:4244)

//'wsprintf': name was marked as #pragma deprecated

#pragma warning(disable:4995)

//unary minus operator applied to unsigned type, result still unsigned #pragma warning(disable:4146)

//'argument' : conversion from 'size_t' to 'int', possible loss of data #pragma warning(disable:4267)

//nonstandard extension used : nameless struct/union

#pragma warning(disable:4201)

///////////////////////// Pragma message helper macro /////////////////////////

/*

When the compiler sees a line like this: #pragma chMSG(Fix this later)

it outputs a line like this:

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

You can easily jump directly to this line and examine the surrounding code. */

#define chSTR2(x) #x #define chSTR(x) chSTR2(x)

#define chMSG(desc) message(__FILE__ "(" chSTR(__LINE__) "):" #desc)

////////////////////////////// chINRANGE Macro ////////////////////////////////

// This macro returns TRUE if a number is between two others

#define chINRANGE(low, Num, High) (((low) <= (Num)) && ((Num) <= (High)))

///////////////////////////// chSIZEOFSTRING Macro ////////////////////////////

// This

macro evaluates to the number of bytes

needed by a string.

#define

chSIZEOFSTRING(psz) ((lstrlen(psz) +

1) * sizeof(TCHAR))

 

 

 

/////////////////// chROUNDDOWN & chROUNDUP inline functions //////////////////

// This inline function rounds a value down to the nearest multiple template <class TV, class TM>

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

inline TV chROUNDDOWN(TV Value, TM Multiple) { return((Value / Multiple) * Multiple);

}

// This inline function rounds a value down to the nearest multiple template <class TV, class TM>

inline TV chROUNDUP(TV Value, TM Multiple) { return(chROUNDDOWN(Value, Multiple) +

(((Value % Multiple) > 0) ? Multiple : 0));

}

///////////////////////////// chBEGINTHREADEX Macro ///////////////////////////

//This macro function calls the C runtime's _beginthreadex function.

//The C runtime library doesn't want to have any reliance on Windows' data

//types such as HANDLE. This means that a Windows programmer needs to cast

//values when using _beginthreadex. Since this is terribly inconvenient,

//I created this macro to perform the casting.

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),

\

(unsigned)

(dwCreateFlags),

\

(unsigned *)

(pdwThreadId)))

 

 

 

 

////////////////// DebugBreak Improvement for x86 platforms ///////////////////

#ifdef _X86_

#define DebugBreak() _asm { int 3 } #endif

/////////////////////////// Software Exception Macro //////////////////////////

// Useful macro for creating your own software exception codes #define MAKESOFTWAREEXCEPTION(Severity, Facility, Exception) \

((DWORD) ( \

 

 

 

 

 

/* Severity code

*/

(Severity

) |

\

/* MS(0) or Cust(1) */

(1

<< 29)

|

\

/* Reserved(0)

*/

(0

<< 28)

|

\

/* Facility code

*/

(Facility

<< 16)

|

\

/* Exception code

*/

(Exception

<< 0)))

 

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

/////////////////////////// Quick MessageBox Macro ////////////////////////////

inline void chMB(PCSTR szMsg) { char szTitle[MAX_PATH];

GetModuleFileNameA(NULL, szTitle, _countof(szTitle)); MessageBoxA(GetActiveWindow(), szMsg, szTitle, MB_OK);

}

//////////////////////////// Assert/Verify Macros /////////////////////////////

inline void chFAIL(PSTR szMsg) { chMB(szMsg);

DebugBreak();

}

// Put up an assertion failure message box.

inline void chASSERTFAIL(LPCSTR file, int line, PCSTR expr) { char sz[2*MAX_PATH];

wsprintfA(sz, "File %s, line %d : %s", file, line, expr); chFAIL(sz);

}

//Put up a message box if an assertion fails in a debug build. #ifdef _DEBUG

#define chASSERT(x) if (!(x)) chASSERTFAIL(__FILE__, __LINE__, #x) #else

#define chASSERT(x) #endif

//Assert in debug builds, but don't remove the code in retail builds. #ifdef _DEBUG

#define chVERIFY(x) chASSERT(x) #else

#define chVERIFY(x) (x) #endif

/////////////////////////// chHANDLE_DLGMSG Macro /////////////////////////////

//The normal HANDLE_MSG macro in WindowsX.h does not work properly for dialog

//boxes because DlgProc returns a BOOL instead of an LRESULT (like

//WndProcs). This chHANDLE_DLGMSG macro corrects the problem:

#define chHANDLE_DLGMSG(hWnd, message, fn)

\

case (message): return (SetDlgMsgResult(hWnd, uMsg,

\

HANDLE_##message((hWnd), (wParam), (lParam), (fn))))

 

 

 

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

//////////////////////// Dialog Box Icon Setting Macro ////////////////////////

// Sets the dialog box icons

inline void chSETDLGICONS(HWND hWnd, int idi) { SendMessage(hWnd, WM_SETICON, ICON_BIG, (LPARAM)

LoadIcon((HINSTANCE) GetWindowLongPtr(hWnd, GWLP_HINSTANCE), MAKEINTRESOURCE(idi)));

SendMessage(hWnd, WM_SETICON, ICON_SMALL, (LPARAM) LoadIcon((HINSTANCE) GetWindowLongPtr(hWnd, GWLP_HINSTANCE), MAKEINTRESOURCE(idi)));

}

/////////////////////////// Common Linker Settings ////////////////////////////

#pragma comment(linker, "/nodefaultlib:oldnames.lib")

// Needed for supporting XP/Vista styles. #if defined(_M_IA64)

#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='IA64' publicKeyToken='6595b64144ccf1df' language='*'\"")

#endif

#if defined(_M_X64)

#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.6000.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'\"")

#endif

#if defined(M_IX86)

#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"")

#endif

///////////////////////////////// End of File /////////////////////////////////

Оглавление

 

П Р И Л О Ж Е Н И Е Б Распаковщики сообщений, макросы для дочерних элементов

 

управления и API-макросы ..........................................................................................................

873

Макросы — распаковщики сообщений................................................................................

874

Макросы для дочерних элементов управления.................................................................

876

API-макросы..............................................................................................................................

877

П Р И Л О Ж Е Н И Е Б

Распаковщики сообщений, макросы для дочерних элементов управления

и API-макросы

Используя в программах-примерах язык C/C++ совместно с распаковщиками сообщений, я стремился показать всем эти малоизвестные, но очень полезные макросы.

Распаковщики содержатся в файле WindowsX.h, поставляемом с Microsoft Visual С++. Этот файл обычно включается сразу после Windows.h, и он — не что иное, как набор директив #define, определяющих макросы трех групп: распаковщики сообщений (message crackers), макросы для дочерних элементов управления

(child control macros) и API-макросы (API macros). Эти макросы дают разработчи-

кам ряд дополнительных возможностей.

Сокращают число явных преобразований типов в коде программы, а также возникающих при этом ошибок. Большой объем таких преобразований — одна из насущных проблем в программировании для Windows на C/C++. Редко когда вызов Windows-функции не требует преобразования типов. В то же время явных преобразований типов следует избегать — они препятствуют выявлению возможных ошибок при компиляции. Явно преобразуя типы, вы говорите компилятору: «Здесь передается неправильный тип, но это нормально. Мне лучше знать, что надо, а что не надо». Однако при большом количестве таких преобразований ошибиться очень легко. А потому позвольте компилятору максимально помочь Вам в выявлении ошибок.

Делают код более читабельным.

Упрощают перенос программ между 32- и 64-разрядными версиями Windows.

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

Они просты и понятны — в конце концов это всего лишь макросы.

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

Их можно применять в коде, написанном как на С, так и на С++, хотя в них нет нужды при работе с библиотекой классов С++.

Если вам понадобится что-то, чего макросы дать не могут, вы сами напишете то, что нужно, следуя модели, используемой в заголовочном файле.

Работая с этими макросами, вам не придется обращаться к справочной информации или запоминать туманные конструкции Windows. Например, многие функции в Windows принимают параметр типа LONG, в младшем слове которого содержится одна величина, а в старшем — совсем другая. Перед вызовом функции из двух этих значений надо собрать одно (типа LONG). Обычно это делается с помощью макроса MAKELONG, определенного в файле WinDef.h. Сколько раз я ошибался при его использовании — не сосчитать, а ведь в результате функция получала неверный параметр. И здесь тоже помогут макро-

сы из WindowsX.h.

Макросы — распаковщики сообщений

Распаковщики сообщений упрощают написание оконных процедур. Последние обычно представляют собой один огромный оператор switch. Мне случалось видеть в оконных процедурах операторы switch, содержавшие свыше 500 строк кода. Все мы знаем, что это образец плохого стиля, но… продолжаем писать именно так. Я и сам этим грешу. А распаковщики заставляют разбивать оператор switch на небольшие функции — по одной на оконное сообщение. Это значительно улучшает внутреннюю структуру кода.

Другая проблема с оконными процедурами в том, что у каждого сообщения есть параметры wParam и lParam, смысл которых зависит от типа сообщения. Бывает, что в сообщении WM_COMMAND параметр wParam содержит два разных значения. Старшее его слово — код уведомления, младшее — идентификатор элемента управления. Или наоборот? Вечно я это путаю. Но если вы используете распаковщики, Вам не придется запоминать эту информацию или искать ее в справочниках. Такие макросы названы распаковщиками потому, что они распаковывают параметры заданного сообщения. Чтобы обработать WMCOMMAND, Вы просто пишете функцию, которая выглядит примерно так:

void Cls_OnCommand(HWND hWnd, int id, HWND hWndCtl, UINT codeNotify) {

switch (id) {

case ID_SOMELISTBOX:

If (codeNotify != LBN_SELCHANGE) break;

Приложение Б. Распаковщики сообщений, макросы для дочерних элементов управления и

API-макросы.docx 875

// обрабатываем LBM_SELGHANGE break;

case ID_SOMEBUTTON: break;

Смотрите, как просто! Распаковщики берут параметры wParam и lParam, расщепляют их на отдельные компоненты и вызывают вашу функцию.

Чтобы использовать распаковщики, нужно внести кое-какие изменения в оператор switch оконной процедуры:

LRESULT WndProc (HWND hWnd, UINT uMsg,

WPARAM wParam, LPARAM lParam) {

switch (uMsg) {

HANDLE_MSG(hWnd, WM_COMMAND, Cls_OnCommand);

HANDLE_MSG(hWnd, WM_PAINT, Cls_OnPaint);

HANDLE_MSG(hWnd, WM_DESTROY, Cls_OnDestroy); default:

return(DefWindowProc(hWnd, uMsg, wParam, lParam));

}

}

В файле WindowsX.h макрос HANDLEMSG определен так:

#define HANDLE_MSG(hwnd, message, fn) \ case (message): \

return HANDLE_##message((hwnd), (wParam), (lParam), (fn))

Для сообщения WMCOMMAND эта строка после обработки препроцессором выглядит как:

case (WM_COMMAND):

return HANDLE_WM_COMMAND((hwnd), (wParam), (lParam), (Cls_OnCommand));

Макросы HANDLE_WM_ определены тоже в WindowsX.h. Это и есть настоящие распаковщики сообщений. Они распаковывают содержимое параметров wParam и lParam, выполняют нужные преобразования типов и вызывают соответствующую функцию — обработчик сообщения (вроде показанной ранее функции

ClsOnCommand). Вот макрос HANDLE_WM_COMMAND:

#define HANDLE_WM_COMMAND(hwnd, wParam, lParam, fn) \

( (fn) ((hwnd), (int) (L0W0RD(wParam)), (HWND)(lParam), (UINT) HIWORD(wParam)), OL)

Результат раскрытия препроцессором этого макроса — вызов функции Cls_OnCommand, которой передаются (после соответствующих преобразований типов) распакованные части параметров wParam и lParam.

Чтобы использовать распаковщик для обработки сообщения, откройте файл WindowsX.h и найдите сообщение, которое вы собираетесь обрабатывать. Например, если вас интересует сообщение WM_COMMAND, ищите фрагмент файла, содержащий строки:

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