В первой статье по WINAPI мы разобрали программу, которая выводит окно с сообщением. Кроме того, после нажатия «ОК» окно закрывалось. И это всё, что она могла. Нетрудно догадаться, что при помощи Win32 API можно реализовывать более информативные окна. Поговорим о создании настоящей оконной процедуры.
Обобщённый алгоритм создания оконной процедуры в WINAPI:
- Создать две функции: WinMain() – основная функция с параметрами, оговоренными в первой статье – аналог main в консоли, и функцию обрабатывающую процессы (название своё, пусть будет традиционно – WndProc() ), потоки сообщений к/от ОС Windows.
- Создать дескриптор окошка hMainWnd и зарегистрировать класс окошка WNDCLASSEX (то есть указать характеристики окна, которое будем создавать). Должен содержаться в WinMain.
- Создать оболочку нашего окна. Должна содержаться в WinMain.
- Создать циклы для обработки сообщений. Содержатся в функции WndProc.
- Создать функцию для вывода окна ShowWindow и другие вспомогательные.
Теперь будет трудновато, потому что сейчас появятся различные функции с кучей параметров, классы, которые следует знать, понимать, ну и некоторые другие премудрости языков С и С++.
Для начала опишем общую структуру функции WinMain()
:
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPreviousInst, LPSTR lpCommandLine, int nCommandShow ) { // создаём дескриптор окна // описываем класс окна // создаём окошко, показываем его на экране // возвращаем значение при неудаче или при выходе }
Для начала создадим дескриптор окна:
HWND hMainWnd; // создаём дескриптор будущего окошка // и т.д.
Чтобы инициализировать поля класса окна, нужно создать его экземпляр, далее через него заполнить поля класса.
Объявление этого класса в windows.h
выглядит так:
struct tagWNDCLASSEX{ UINT cbSize; // величина структуры (в байтах) UINT style; // стиль класса окошка WNDPROC WndProc; // указатель на имя пользовательской функции int cbWndExtra; // число освобождаемых байтов в конце структуры int cbClsExtra; // число освобождаемых байтов при создании экземпляра приложения HICON hIcon; // дескриптор значка HICON hIconMini; // .... маленького значка (в трэе) HCURSOR hCursor; // .... курсора мыши HBRUSH hbrBack; // .... цвета фона окошка HINSTANCE hInst; // .... экземпляра приложения LPCTSTR lpszClassName; // указатель на const-строку, содержащюю имя класса LPCTSTR lpszMenuName; // указатель на const-строку, содержащюю имя меню, применяемого для класса }WNDCLASSEX;
То есть мы должны создать переменную типа WNDCLASSEX
, обычно это wc
, затем через неё инициализировать поля класса, примерно так:
// создавали дескриптор окна WNDCLASSEX wc; // создаём экземпляр, для обращения к членам класса WNDCLASSEX wc.cbSize = sizeof(wc); // размер структуры (в байтах) wc.lpfnWndProc = WndProc; // указатель на пользовательскую функцию // и т.д.
Это необходимо, чтобы использовать этот класс в дальнейшем (если мы захотим). Это будет шаблоном для создания кучи окон. Конечно, на первых порах нам не нужно столько окошек. Но регистрация нужна однозначно! Это — формальная сторона. Мы даже можем задать параметры по умолчанию при инициализации полей, а не придумывать каким должно быть окно (о параметрах, которыми должны инициализировать поля класса мы обсудим в следующем уроке).
Итак, ещё раз: если хоть одно поле класса не будет инициализировано, то окно не создастся. Для проверки существует полезная функция RegisterClassEx().
Это и есть следующий этап после заполнения полей класса WNDCLASSEX
: обязательная проверка регистрации класса:
// регистрировали класс if(!RegisterClassEx(&wc)){ // в случае отсутствия регистрации класса: MessageBox(NULL, L"Не получилось зарегистрировать класс!", L"Ошибка", MB_OK); return NULL; // возвращаем, следовательно, выходим из WinMain } // и т.д.
Как видим, в этом коде всё просто. Если RegisterClassEx()
возвращает адекватный ATOM
(таково описание:
ATOM WINAPI RegisterClassEx(const WNDCLASSEX *lpWindowClass);
), то мы выводим соответствующее сообщение, которое мы разбирали в первой статье. Её параметр – это ссылка на экземпляр класса WNDCLASSEX
wc
.
Следующее, что мы обязаны сделать в функции WinMain()
– вызвать функцию CreateWindow()
и присвоить её значение дескриптора, который мы создавали на первом этапе. Примерно это так (описание всех этих параметров будет в следующем уроке):
// проверяли, зарегистрирован ли класс hMainWnd = CreateWindow(szClassName, // имя класса L"Полноценная оконная процедура", // имя окна (то что сверху) WS_OVERLAPPEDWINDOW | WS_VSCROLL, // режимы отображения окошка CW_USEDEFAULT, // положение окна по оси х (по умолчанию) NULL, // позиция окна по оси у (раз дефолт в х, то писать не нужно) CW_USEDEFAULT, // ширина окошка (по умолчанию) NULL, // высота окна (раз дефолт в ширине, то писать не нужно) HWND(NULL), // дескриптор родительского окошка (у нас нет род. окон) NULL, // дескриптор меню (у нас его нет) HINSTANCE(hInst), // .... экземпляра приложения NULL); // ничего не передаём из WndProc // и т.д.
Описание CreateWindow()
в windows.h
выглядит так:
HWND CreateWindow( LPСTSTR lpClassName, // имя нашего класса LPCTSTR lpWindowName, // название окошка (надпись сверху) DWORD dwStyle, // стиль окошка int x, // позиция окошка по оси х int y, // позиция окна по оси у (отсчёт вниз) int nWidth, // ширина окошка int nHeight, // высота окошка HWND hWndParent, // идентификатор родительского окошка HMENU hMenu, // .....меню HINSTANCE hInst, // дескриптор экз-ра прил-ния LPVOID lParam ); // указатель на данные, передаваемые из пользовательской функции
В случае удачи, функция в переменную hMainWnd
возвратит не NULL
. Наверное, не трудно догадаться, что надо сделать проверку (по аналогии с RegisterClassEx())
:
// создали окно if(!hMainWnd){ // в случае некорректного создания окна (неверные параметры и тп): MessageBox(NULL, L"Не получилось создать окно!", L"Ошибка", MB_OK); return NULL; // выходим из приложения } // и т.д.
После чего нам необходимо вызвать две функции:
// проверяли, создано ли окно ShowWindow(hMainWnd, nCommandShow); UpdateWindow(hMainWnd); // и т.д.
Подробно описывать их нет надобности, так как имеют несколько параметров.
Первая функция отображает окно на экране ПК. Её первый параметр – дескриптор окошка (он возвращался CreateWindow()
). Второй параметр – стиль отображения. При первом запуске окна должен быть равен последнему параметру функции WinMain()
, а в последующие разы можно вписывать свои данные (об этом в следующих статьях).
Вторая функция – видно по названию, отвечает за обновления окошка на экране при сворачиваниях или при динамической информации. Его параметр – всё тот же дескриптор окна.
В функции остался цикл и возвращение значение функции:
// показывали, обновляли окно while(GetMessage(&msg, NULL, NULL, NULL)){ TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; // всё!
Так как объекты структуры MSG
связаны с рассылкой сообщений системному окну и наоборот и это всё содержится в пользовательской функции WndProc
, поэтому описание этих функций будет в следующем уроке.
Где-то за 3 урока программа, выводящая полноценную оконную процедуру на экран, будет разобрана и у Вас появятся сведения о том, каким образом они создаются.
Напоследок модифицированная программа, выводящая окно с сообщением (из первой статьи) с другими кнопками:
#include <windows.h> // содержит API // Основная функция: int WINAPI WinMain (HINSTANCE hInst, // дескриптор экземпляра приложения HINSTANCE hPreviousInst, // в Win32 не используется, но объявление нужно LPSTR lpCommandLine, // нужен для запуска окошка в режиме командной строки int nCommandShow) // режим отображения окна { int result = MessageBox(NULL, L"Вам нравится WINAPI?!", L"Задача", MB_ICONQUESTION | MB_YESNO); switch (result) { case IDYES: MessageBox (NULL, L"Продолжайте в том же духе!!!", L"Ответ", MB_OK| MB_ICONASTERISK); break; case IDNO: MessageBox (NULL, L"Очень жаль!!!", L"Ответ", MB_OK| MB_ICONSTOP); break; } return NULL; }
Скомпилируйте данный код, и Вы обнаружите, что с функцией MessageBox()
тоже можно поэкспериментировать.
Черточки между параметрами в строках 10,14 и 17 означают, что параметры используются вместе.
Третьим параметром мы можем записать следующие идентификаторы:
параметры кнопок:
параметры пиктограммы:
А возвращать значения при нажатии вышеуказанных кнопок функция MessageBox будет такие:
Поэкспериментируйте с данными идентификаторами. С параметрами отображения мы будем иметь дело во многих функциях. На этом всё.
Комментарии
Программист
Как же хреново был обяснен один момент, цытируууую:» В функции остался цикл и возвращение значение функции:».Сразу же два вопроса:Какая нафиг функция , и где ее взять.Это читать не возможно
npavelFax
Официальная работа в интернете с обучением.
BittleF1998
я конечно всё понимаю, но что это?
struct tagWNDCLASSEX{
UINT cbSize; // величина структуры (в байтах)
UINT style; // стиль класса окошка
WNDPROC WndProc; // указатель на имя пользовательской функции
int cbWndExtra; // число освобождаемых байтов в конце структуры
int cbClsExtra; // число освобождаемых байтов при создании экземпляра приложения
HICON hIcon; // дескриптор значка HICON hIconMini; // …. маленького значка (в трэе)
HCURSOR hCursor; // …. курсора мыши
HBRUSH hbrBack; // …. цвета фона окошка
HINSTANCE hInst; // …. экземпляра приложения
LPCTSTR lpszClassName; // указатель на const-строку, содержащюю имя класса
LPCTSTR lpszMenuName; указатель на const-строку, содержащюю имя меню, применяемого для класса
}WNDCLASSEX;
на том же мсдн’е всё по-другому.
https://msdn.microsoft.com/en-us/library/windows/desktop/ms633577(v=vs.85).aspx
и в Winuser.h тоже!
и даже у вас в примере на следующей странице всё по-другому(правильно)
почему не изменено здесь?
чего спрашиваю-то- вы ж меня в ступор на час загнали с этим примером!
Герман Самсонов
так и не ответили(
Программист
Чтобы не имет с этим проблем , просто инициализируйте со списком параметров «по нулям»: