Рихтер Дж., Назар К. - Windows via C C++. Программирование на языке Visual C++ - 2009
.pdf
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, ищите фрагмент файла, содержащий строки:
