Windows поддерживает два типа приложений: основанные на графическом интерфейсе (graphical user interface, GUI) и консольные (console user interface, CUI).
Приложения первого типа создают окна, имеют меню, взаимодействуют с пользователем через диалоговые окна. Почти все стандартные программы Windows являются GUI-приложениями.
Приложения консольного типа работают в текстовом режиме.
Во всех Windows-приложениях должна быть входная функция. Существует четыре такие функции:
1 2 3 4 5 6 7 | int WINAPI WinMain( HINSTANCE hinstExe, HINSTANCE,PSTR pszCmdLine, int nCmdShow); int WINAPT wWinMain( HINSTANCE hinstExe, HINSTANCE,PWSTR pszCmdLine, int nCmdShow); int __cdecl main( int argc, char *argv[], char *envp[]); int _cdecl wmain( int argc, wchar_t *argv[], wchar_t *envp[]); |
GUI-приложение, работающее с ANSI-символами и строками используют WinMain
GUI-приложение, работающее с Unicode-символами и строками используют wWinMain
CUI-приложение, работающее с ANSI-символами и строками используют main
CUI-приложение, работающее с Unicode-символами и строками используют wmain
В отличие от традиционного программирования на основе линейных алгоритмов, программы для Windows строятся по принципам событийно-управляемого программирования, при котором поведение компонента системы определяется набором внешних событий и реакций компонента на них. Такими компонентами в Windows являются окна. С каждым окном в Windows связана определенная функция обработки событий. События для окон называются сообщениями. Сообщение относится к тому или иному типу, идентифицируемому определенным кодом (32-битным целым числом), и сопровождается парой 32-битных параметров WPARAM и LPARAM, интерпретация которых зависит от типа сообщения. В заголовочном файле windows.h для кодов сообщений определены константы с интуитивно понятными именами:
#define WM_CREATE 0x0001 сообщение о создании окна
#define WM_DESTROY 0x0002 сообщение об уничтожении окна
#define WM_SIZE 0x0005 сообщение об изменении размеров окна
#define WM_COMMAND 0x0111 сообщение от команды меню или управляющего элемента
Таким образом, задача любого приложения — создать главное окно и сообщить Windows функцию обработки событий для этого окна.
Для стандартных управляющих элементов (библиотека Common Controls Library — COMCTL32.DLL) в Windows имеются предопределенные обработчики событий, которые при наступлении событий сообщают информацию окну, содержащему этот управляющий элемент. Стандартная библиотека Common Dialog Box Library (COMDLG32.DLL) содержит несколько готовых диалоговых окон с обработчиками: диалоги выбора файла, настроек печати, выбора шрифта, выбора цвета и др.
Программа для Win32 обычно состоит из следующих блоков:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | #include int WINAPI WinMain(HINSTANCE hInst, HINSTANCE, LPSTR pszCmdLine, int nCmdShow) { Блок инициализации: создание класса главного окна, создание главного окна, загрузка ресурсов и т.п. Цикл обработки событий: MSG msg; while (GetMessage(&msg,(HWND)NULL,0,0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { Обработка сообщений главного окна switch (msg) { case WM_CREATE: ... return 0; case WM_COMMAND: ... return 0; case WM_DESTROY: ... PostQuitMessage(0); return 0; ... } return DefWindowProc(hWnd,message,wParam,lParam); } |
Каждое окно принадлежит определенному классу окон. Обычно каждое приложение создает для главного окна программы свой класс. Если приложению требуются дополнительные нестандартные окна, оно регистрирует другие классы.
Стандартные диалоги и управляющие элементы принадлежат к предопределенным классам окон, для них не надо регистрировать новые классы.
Чтобы определить новый класс окон, надо заполнить структуру WNDCLASSEX, содержащую следующие поля:
UINT cbSize — размер структуры
UINT style — стиль класса окон
WNDPROC lpfnWndProc — процедура обработки событий окна
int cbClsExtra — размер дополнительной памяти в системной структуре класса для данных пользователя
int cbWndExtra — размер дополнительной памяти в системной структуре окна для данных пользователя
HINSTANCE hInstance — дескриптор модуля (экземпляра программы) в котором реализована процедура обработки
HICON hIcon — дескриптор иконки окна
HCURSOR hCursor — дескриптор курсора мыши для окна
HBRUSH hbrBackground — дескриптор кисти для закрашивания фона окна
LPCSTR lpszMenuName — имя ресурса, содержащего меню окна
LPCSTR lpszClassName — имя класса
HICON hIconSm — дескриптор иконки
Класс регистрируется при помощи функции:
1 | ATOM RegisterClassEx(CONST WNDCLASSEX *lpwcx) |
При успешном завершении функция возвращает целочисленный код, соответствующий строке-имени класса в общесистемной таблице строк (такой код называется атомом). При ошибке возвращается 0.
Для создания окна вызывается функция:
1 2 3 4 5 6 7 8 9 10 11 12 | HWND WINAPI CreateWindow( LPCSTR lpClassName, //имя класса LPCSTR lpWindowName, //имя окна (заголовок) DWORD dwStyle, //стиль (поведение) окна int x, //горизонтальная позиция окна на экране int y, //вертикальная позиция окна на экране int nWidth, //ширина окна int nHeight, //высота окна HWND hWndParent, //дескриптор родительского окна HMENU hMenu, //дескриптор меню HANDLE hInstance, //дескриптор экземпляра программы LPVOID lpParam ) |
Вместо параметров x, y, nWindth, nHeight допустимо передавать константу CW_USEDEFAULT, позволяющую операционной системе задать эти числа по ее усмотрению.
Интерпретация кода стиля определяется классом окна. Стиль определяет не только оформление окна, но и его поведение. Общие для всех классов константы стилей (при необходимости объединяются операцией побитовое ИЛИ):
WS_DISABLED — при создании окно заблокировано (не может получать реакцию от пользователя)
WS_VISIBLE — при создании окно сразу же отображается (не надо вызывать ShowWindow)
WS_CAPTION — у окна есть строка заголовка
WS_SYSMENU — у окна есть системное меню
WS_MAXIMIZEBOX — у окна есть кнопка разворачивания
WS_MINIMIZEBOX — у окна есть кнопка сворачивания
WS_SIZEBOX или WS_THICKFRAME — у окна есть рамка изменения размеров
WS_BORDER — у окна есть рамка (не подразумевает изменение размеров)
WS_HSCROLL или WS_VSCROLL — у окна есть горизонтальная или вертикальная прокрутка
WS_OVERLAPPED или WS_TILED — «перекрываемое» окно — обычное окно с рамкой и строкой заголовка
WS_POPUP — «всплывающее» окно
WS_OVERLAPPEDWINDOW — «перекрываемое» окно с системным меню, кнопками сворачивания/разворачивания, рамкой изменения размеров, короче, типичный стиль для главного окна приложения.
Во время выполнения функции CreateWindow процедуре обработки событий окна посылается сообщение WM_CREATE. При успешном выполнении функции возвращается дескриптор созданного окна, при неудаче — NULL.
После создания окна его нужно отобразить, если только оно не создано со стилем WS_VISIBLE:
1 | BOOL ShowWindow( HWND hWnd,int nCmdShow) |
Второй параметр этой функции — код состояния отображения окна. В качестве этого кода можно взять значение четвертого параметра, с которым была запущена функция WinMain. Другие возможные значения этого параметра:
SW_SHOW — отобразить и активировать окно
SW_HIDE — скрыть окно
SW_MAXIMIZE — развернуть окно на весь экран
SW_RESTORE — активировать окно и отобразить его в размерах по умолчанию
SW_MINIMIZE — свернуть окно
Если перед вызовом этой функции окно было видимым, функция возвращает TRUE, если же окно было скрыто — FALSE.
Функция UpdateWindow непосредственно вызывает процедуру обработки событий указанного окна с сообщением WM_PAINT (минуя очередь сообщений приложения):
1 | BOOL UpdateWindow(HWND hWnd) |
Windows использует два способа доставки сообщений процедуре обработки событий окна:
— непосредственный вызов процедуры обработки событий (внеочередные или неоткладываемые сообщения — nonqueued messages);
— помещение сообщения в связанный с данным приложением буфер типа FIFO, называемый очередью сообщений — message queue (откладываемые сообщения — queued messages).
К внеочередным сообщениям относятся те сообщения, которые непосредственно влияют на окно, например, сообщение активации окна WM_ACTIVATE и т.п. Кроме того, вне очереди сообщений обрабатываются сообщения, сгенерированные различными вызовами Win32 API, такими как SetWindowPos, UpdateWindow, SendMessage, SendDlgItemMessage.
К откладываемым сообщениям относятся сообщения, связанные с реакцией пользователя: нажатие клавиш на клавиатуре, движение мышки и клики.
Чтобы извлечь сообщение из очереди, программа вызывает функцию
1 2 3 4 5 6 | BOOL WINAPI GetMessage( MSG *lpmsg, //сюда попадает сообщение со всякими параметрами HWND hw, //извлекать только сообщения для указанного окна UINT wMsgFilterMin, //фильтр сообщений (нам не надо - ставим 0) UINT wMsgFilterMax //фильтр сообщений (нам не надо - ставим 0) ) |
Эта функция возвращает FALSE, если получено сообщение WM_QUIT, и TRUE в противном случае. Очевидно, что условием продолжения цикла обработки событий является результат этой функции. Если приложение хочет завершить свою работу, оно посылает само себе сообщение WM_QUIT при помощи функции
1 | void WINAPI PostQuitMessage(int nExitCode) |
Ее параметр — статус выхода приложения. Обычно эта функция вызывается в ответ на сообщение об уничтожении окна WM_DESTROY.
После извлечения сообщения из очереди следует вызвать функцию TranslateMessage, переводящую сообщения от нажатых клавиш в удобоваримый вид, а затем DispatchMessage, которая определяет предназначенное этому сообщению окно и вызывает соответствующую процедуру обработки событий.
1 2 | BOOL WINAPI TranslateMessage(const MSG *lpmsg) LONG WINAPI DispatchMessage(const MSG *lpmsg) |
Результат возврата соответствует значению, которое вернула процедура обработки событий (обычно никому не нужен).
Процедура обработки сообщений окна должна быть объявлена по следующему прототипу:
1 | LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) |
Значения параметров: hWnd — дескриптор окна, которому предназначено сообщение, message — код сообщения, wParam и lParam — 32-битные параметры сообщения, интерпретация которых зависит от кода сообщения. Зачастую старший/младший байт или старшее/младшее слово параметров сообщения несут независимый смысл,тогда удобно использовать определенные в windows.h макросы:
1 2 3 4 | #define LOBYTE(w) ((BYTE) (w)) #define HIBYTE(w) ((BYTE) (((WORD) (w) >> 8) & 0xFF)) #define LOWORD(l) ((WORD) (l)) #define HIWORD(l) ((WORD) (((DWORD) (l) >> 16) & 0xFFFF)) |
Например, сообщение WM_COMMAND посылается окну в трех случаях:
— пользователь выбрал какую-либо команду меню;
— пользователь нажал «горячую» клавишу (accelerator);
— в дочернем окне произошло определенное событие.
При этом параметры сообщения интерпретируются следующим образом. Старшее слово параметра WPARAM содержит: 0 в первом случае, 1 во втором случае и код события в третьем случае. Младшее слово WPARAM содержит целочисленный идентификатор пункта меню, «горячей» клавиши или дочернего управляющего элемента. Параметр LPARAM в первых двух случаях содержит NULL, а в третьем случае — дескриптор окна управляющего элемента.
Процедура обработки событий должна вернуть определенное 32-битное значение, интерпретация которого также зависит от типа сообщения. В большинстве случаев, если сообщение успешно обработано, процедура возвращает значение 0.
Процедура обработки событий не должна игнорировать сообщения. Если процедура не обрабатывает какое-то сообщение, она должна вернуть его системе для обработки по умолчанию. Для этого вызывается функция: DefWindowProc(hWnd, message, wParam, lParam);
1 | LRESULT WINAPI DefWindowProc(HWND hWnd, UINT message,WPARAM wParam, LPARAM lParam) |
0 Комментарии。