В первой статье по 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 тоже!
и даже у вас в примере на следующей странице всё по-другому(правильно)
почему не изменено здесь?
чего спрашиваю-то- вы ж меня в ступор на час загнали с этим примером!
Герман Самсонов
так и не ответили(
Программист
Чтобы не имет с этим проблем , просто инициализируйте со списком параметров «по нулям»:
WNDCLASSW wc = {0};