Четыре слона оконных приложений
Действительно, оконное или графическое приложение Windows строится на четырех составляющих. Прежде, чем мы увидим в действии, разрешите кратко описать их. Главный объект приложения - окна (или окон). Все крутится вокруг этого объекта. Любое окно должно быть в начале создано. Перед созданием окна регистрируется так называемый класс окон. В нем указываетмя некоторый набор свойств, которые получат окна, создаваемые на основе этого класса. И так класс - это первый слон. После того, как класс удачно зарегистрирован, можно создавать окно. Само созданное окно и есть второй слон. При создании окна мы также указываем определенный набор свойств. Созданным окном надо как-то управлять. Для этого служат две программные структуры. Это два последних слона. Первая программная структра - оконная функция. Эта функция служит для обработки сообщений, приходящих в адрес окна. Скажем так - это реакция на событие. Последний слон - это структура, называемая циклом сообщений. Цель этой структур (а это действительно цикл) - отловить сообщение, которое находится в очереди для данного приложения, и отослать нужному окну. Точнее функции окна. При этом с сообщением могут производиться некоторые преобразования, а могут и не производиться. Вот, собственно и вся 'общая теория'. Разумеется, она обставлена таким количеством деталей и нюансов, что нужно целую книгу писать. Но лучше перейти к практике и практикуясь познавать все эти детали.

Ну чтож, чтобы увидеть всех наших слонов в действии, рассмотрим очень простой пример. Окно, без всяких элементов управления, что называется 'в чистом виде'. Программа представлена внизу, а потом я ее уже и прокомментирую.

#include <Windows.h>
LRESULT CALLBACK winproc( HWND, UINT, WPARAM, LPARAM);
WNDCLASSEXW ws; //струкутра для регистрации класса окон
wchar_t * wn = L"class1"; //имя класса
MSG msg; //для сообщения
HWND hwnd; //для дескриптора окна
int CALLBACK WinMain(
_In_ HINSTANCE hInstance,
_In_ HINSTANCE hPrevInstance,
_In_ LPSTR lpCmdLine,
_In_ int nCmdShow
)
{
ws.cbSize = sizeof(WNDCLASSEXW);
ws.cbClsExtra = 0;
ws.cbWndExtra = 0;
ws.hIcon = LoadIcon( NULL, IDI_APPLICATION );
ws.hCursor = LoadCursor( NULL, IDC_ARROW );
ws.hbrBackground = CreateSolidBrush(RGB(0, 80, 80));
ws.hIcon = NULL;
ws.hInstance = hInstance;
ws.lpszClassName = wn;
ws.lpszMenuName = NULL;
ws.lpfnWndProc = (WNDPROC)winproc;
ws.style = CS_VREDRAW|CS_HREDRAW;
//зарегистрировать класс окна
if(RegisterClassExW(&ws)==0){
MessageBoxExW(0,L"Ошибка регистрации!",L"Сообщение", 0, 0);
return 0;
};
//создать окно
hwnd= CreateWindowExW(0, //расширенный стиль
wn, //имя класса
L"Окно в 64-битово системе!", //заголовок окна
WS_OVERLAPPEDWINDOW, //стиль окна
10, //x
10, //y
400, //ширина
300, //высота
NULL, //дескриптор родительского окна
NULL, //дескриптор меню
hInstance, //дескриптор приложения
NULL); //параметр приходящий на функцию окна вместе с сообщением WM_CREATE
//сделать окно видимым
ShowWindow( hwnd, nCmdShow );
//обновить содержимое
UpdateWindow(hwnd );
//цикл обработки сообщений
while ( GetMessage( &msg, NULL, NULL, NULL ) ) {
TranslateMessage( &msg );
DispatchMessage( &msg );
}
//выход из программы
return (int)msg.wParam;
}
//функция окна
LRESULT CALLBACK winproc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch ( uMsg ) {
case WM_CREATE:
MessageBoxExW(0,L"WM_CREATE",L"Сообщение", 0, 0);
break;
case WM_SHOWWINDOW:
MessageBoxExW(0,L"WM_SHOWWINDOW",L"Сообщение", 0, 0);
break;
case WM_CLOSE:
MessageBoxExW(0,L"WM_CLOSE",L"Сообщение", 0, 0);
break;
case WM_DESTROY:
MessageBoxExW(0,L"WM_DESTROY",L"Сообщение", 0, 0);
PostQuitMessage(0);
break;
}
return DefWindowProc( hWnd, uMsg, wParam, lParam );
};

Я не буду комментировать каждую строчку программы. Буду исходить из очень простых соображений: во-первых, читатель знаком с языком C, во-вторых, при не понимании чего-либо может обратиться к Интернет. Я же привожу готовые программы, адаптированные для Unicode-кодировки тектовых данных, 64-битовых ОС, а главное для начинающих программировать на API-функциях. В-третьих, в последующих моих этюдах, вы найдете информацию, которая прояснит вам и то, что сейчас было не понятно.

Регистрацию класса окон я пока совсем не буду комментировать. Возьмите это как шаблон и используйте пока не задумываясь. Постепенно станет все ясно. Впрочем по причине 'говорящих' наименований полей структуры ws много ясно уже сразу. API-функция создания окна и так прокоментирована, так что моих пояснений здесь тоже не требуется. Остановимся на цикле обработки сообщений. Функция GetMessage отлавливает очередное сообщение из очереди для данного приложения. Сообщение помещается в структуру msg. Структура содержит 6 полей, с которыми вы постепенно познакомитесь. Резонный вопрос, когда закончится цикл. Цикл заканчивается, когда приложению приходит сообщение WM_QUIT и функция возвращает 0. Функция TranslateMessage в принципе не обязательна. Она преобразует некоторые сообщения, точее посылая дополнительно к ним свои сообщения, которые попадают в тот цикл, но уже при следующем шаге цикла. Мы с ними познакомимся позднее. Функция DispatchMessage отправляет сообщение непосредственно функции окна.

Теперь перейдем к функции окна. Сразу обращусь в конец функции к оператору return DefWindowProc( hWnd, uMsg, wParam, lParam );. Это означает, что после окончания обработки функции полученное сообщение отправляется дальше в систему для последующей обработки уже системой. Если же мы это не сделаем, а закончим работу функции, скажем оператором return 0, то это значит, что система уже не будет обрабатывать это сообщение. Этот прием используют для отмены некоторых действий. Теперь обратимся к началу. Как мы видим, сообщение представляет собой 4 параметра. Первый параметр - дескриптор окна, остальные три содержат информацию, что из себя представляет данное сообщение. Мы постепенно научимся их анализировать. Замечу, кстати, что hWnd это тоже, что hwnd, возвращаемое функцией CreateWindowExW. В нашей программе обрабатываются четыре сообщения. Чтобы было понятно, в каком порядке они приходят, я использовал функцию MessageBoxExW. Вы легко в этом разберетесь. Обращаю ваше мнимание вот на что. Закрытие окна еще не означает закрытие приложения. Мы используем функцию PostQuitMessage, чтобы приложение на самом деле закрылось, а не висело в памяти. Эта функция посылает нашему же приложение сообщение WM_QUIT, которое, как мы знаем, приводит в к выходу из цикла обработки сообщений.

Ну вот пока все. Наслаждайтесь программированием.

 Назад       Заметки       Сайт       Страница-портал       Журнал
(c) Copyright Владислава Юрьевич Пирогов