Немного отступив от темы, давайте вспомним, как начинали программировать на паскале или на С, или еще на чем.
Там все течение аппликации было линейным. В связи с этим все действия и функцию выбора по нажатию клавиш - мы писали сами.
В DOS аппликациях, как и в консольных аппликациях Windows мы имеем дело с программированием линейного типа.
В Windows все совершенно иначе.
Конечно, если взять все библиотеки Microsoft и вашу программу и расположить все в виде кода, мы сможем увидеть туже линейную структуру выполнения программы.
Но это мы забудем, потому что механизмы внутреннего устройства от нас скрыты, а видеть мы будем свою функцию WndProc.
Код WndProc:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
TCHAR szHello[MAX_LOADSTRING];
LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING);
switch (message)
{
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code here...
RECT rt;
GetClientRect(hWnd, &rt);
DrawText(hdc, szHello, strlen(szHello), &rt, DT_CENTER);
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
Давайте теперь разберем ее по косточкам.
Эта функция, по сути, и есть основная часть рабочего приложения. Система Windows предоставляя программе ресурсы должна общаться с ним через какой-либо интерфейс. Этот интерфейс базируется на сообщениях, которые необходимо обрабатывать и которые можно и нужно отсылать в систему для сообщения ей о необходимых действиях. Естественно, посылать можно с любого места программы. А вот для обработки системных сообщений выделяется отдельная функция. Именно о ней мы и говорим.
Не будем пока говорить о том, как попадают системные сообщения в нашу программу. Они попадают и именно в функцию WndProc. Вместе с сообщением Windows передает два абстрактных параметра LPARAM WPARAM. Таким образом, вместе с сообщением идут данные по необходимости. Скажем, при нажатии кнопочки, мы получим сообщение WM_CHAR, а в параметрах код нажатой клавиши и кучу других полезных данных.
Давайте посмотрим, что делает в стартовом минимально необходимом состоянии функция WndProc.
switch (message)
{
Это основной разборщик наших сообщений.
case WM_DESTROY:
Самый простой пример обработки сообщения.
WM_DESTROY - идет без параметров, говорит оно о том, что пользователь или какая либо программа (крайне редкая ситуация - иногда система при перезагрузке) послала приказ закрыться. Нажимая на крестик в правом верхнем углу, мы посылаем в программу именно это сообщение.
Обработка очень проста.
PostQuitMessage(0);
break;
PostQuitMessage(0); посылает в саму программу сообщение для закрытия программы с кодом возврата 0, что говорит о нормальном безошибочном завершении работы.
case WM_PAINT:
Это сообщение сложнее и интересней.
Для понимания сути происходящего мы должны обратится к системным правилам Windows.
Каждый раз, когда система отображает что-либо, например окно аппликации, оно посылает в программу сообщение WM_PAINT для обновления контекста окна. После обработки контекст окна показывается на экране. Естественно, если мы проигнорируем сообщение, то вместо своего окна увидим пустоту.
Для обработки сообщения нам нужно следующее:
hdc = BeginPaint(hWnd, &ps);
Возьмем HANDLE окна hWnd, присланный вместе с сообщением, и с помощью данной функции получим HANDLE на отображаемую часть окна DeviceContent иначе DC.
Параллельно в структуру PAINTSTRUCT загрузятся необходимые данные о нем.
RECT rt;
GetClientRect(hWnd, &rt);
Теперь мы возьмем область клиентской части окна. Это четырехугольник белого цвета без меню и бордеров.
DrawText(hdc, szHello, strlen(szHello), &rt, DT_CENTER);
В данном примере просто напишем текст Hello World
EndPaint(hWnd, &ps);
Закончим процесс обновления экрана. Функция EndPaint занесет в систему все сделанные нами изменения, кроме того, обозначит для Windows, что теперь уже можно выводить наши данные на экран.
Для дальнейшего рассмотрения отвлечемся немного на работу с элементами окна.
Такие элементы как дочерние ( child) внутренние окна, элементы типа меню и т.д. посылают в главное окно свои события вместе с командным сообщением WM_COMMAND.
Это сообщение в своих параметрах несет данные о том, какой элемент управления послал сообщение, и что там произошло.
Для обработки такого сообщения мы должны раскрыть его данные.
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
В верхней части HIWORD два старших байта WPARAM содержат информацию о типе события, в нижней части LOWORD двух младших байтах, ID того элемента, которое послало данное сообщение.
Так как тип события известен - клик на пункте меню мы обработаем только ID.
// Parse the menu selections:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About);
break;
Это нажат пункт "О программе" (About) реакция на который, вызов обычного дочернего диалогового окна с текстом из ресурсов программы по его ID.
DialogBox(hInst, // Инстанс основной программы
IDD_ABOUTBOX, // ID Диалога из ресурсов
hWnd // HANDLE на основное окно для указания чье диалоговое окно выводится
);
About Процедура WndProc - для обработки сообщений диалогом. В коде она есть и мы рассмотрим ее в следующий раз.
case IDM_EXIT:
Нажат пункт меню Выход!
DestroyWindow(hWnd);
Эта функция пошлет сообщение WM_DESTROY всей программе, его мы описали чуть ранее.
return DefWindowProc(hWnd, message, wParam, lParam);
И вот напоследок обязательное завершение любой функции WndProc!
Это вызов главного обработчика Windows, который и проделает все остальные действия. Ведь мы всего лишь петля, часть той структуры обработки сообщения Windows, которая отвечает за наши действия. Все дальнейшие действия по правильному выводу на экран содержимого контекста - закрытие или открытие окон - движение мышки и т.д. должна обрабатывать сама система. Вызовом DefWindowProc - мы отдаем и данные текущего сообщения и наши параметры в систему для дальнейшей обработки.
Если этого не сделать, то сообщение не будет иметь эффекта. Например, если в case WM_DESTROY написать не
PostQuitMessage; break; , а просто
return 0; то программа будет работать но вы не сможете ее закрыть обычным путем.
Автор: Гром