Статья
Версия для печати
Обсудить на форуме
Советы по Windows (часть 2)

Немного отступив от темы, давайте вспомним, как начинали программировать на паскале или на С, или еще на чем.

Там все течение аппликации было линейным. В связи с этим все действия и функцию выбора по нажатию клавиш - мы писали сами.
В 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; то программа будет работать но вы не сможете ее закрыть обычным путем.

Автор: Гром
Версия для печати
Обсудить на форуме