Советы по Windows (часть 2) - ОС и разработчик - Shelek
Немного отступив от темы, давайте вспомним, как начинали программировать на паскале или на С, или еще на чем.

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


Автор: Гром
Information
  • Posted on 01.02.2010 00:40
  • Просмотры: 1886