Создание полноценной оконной процедуры в Win32 API (Часть 1)

В первой статье по WINAPI мы разобрали программу, которая выводит окно с сообщением. Кроме того, после нажатия «ОК» окно закрывалось. И это всё, что она могла. Нетрудно догадаться, что при помощи Win32 API можно реализовывать более информативные окна. Поговорим о создании настоящей оконной процедуры.

Обобщённый алгоритм создания оконной процедуры в WINAPI:

  1. Создать две функции: WinMain() – основная функция с параметрами, оговоренными в первой статье – аналог main в консоли, и функцию обрабатывающую процессы (название своё, пусть будет традиционно – WndProc() ), потоки сообщений к/от ОС Windows.
  2. Создать дескриптор окошка hMainWnd и зарегистрировать класс окошка WNDCLASSEX (то есть указать характеристики окна, которое будем создавать). Должен содержаться в WinMain.
  3. Создать оболочку нашего окна. Должна содержаться в WinMain.
  4. Создать циклы для обработки сообщений. Содержатся в функции WndProc.
  5. Создать функцию для вывода окна 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 означают, что параметры используются вместе.

Третьим параметром мы можем записать следующие идентификаторы:

параметры кнопок:

MB_ABORTRETRYIGNORE — три кнопки: ABORT, RETRY, IGNORE MB_CANCELTRYCONTINUE — три кнопки: CANCEL, TRY, CONTINUE MB_HELP MB_OK MB_OKCANCEL — 2 кнопки: OK, CANCEL MB_RETRYCANCEL — 2 кнопки: RETRY, CANCEL MB_YESNO — 2 кнопки: YES, NO MB_YESNOCANCEL — три кнопки: YES, NO, CANCEL

параметры пиктограммы:

MB_ICONSTOP — выводит крестик MB_ICONQUESTION — …….знак вопроса MB_ICONEXCLAMATION — …….восклицательный знак в треугольнике MB_ICONASTERISK — …….восклицательный знак

 А возвращать значения при нажатии вышеуказанных кнопок функция MessageBox будет такие:

IDABORT — при нажатии на ABORT IDCANCEL – …….на кнопку CANCEL IDCONTINUE – ……..на кнопку CONTINUE IDIGNORE – …….на кнопку IGNORE IDNO – …….на кнопку NO IDOK – …….на кнопку OK IDRETRY – …….на кнопку RETRY IDTRYAGAIN – …….на кнопку TRY AGAIN IDYES – …….на кнопку YES

Поэкспериментируйте с данными идентификаторами. С параметрами отображения мы будем иметь дело во многих функциях. На этом всё.

 

Автор: Marienko L.
Дата: 20.02.2014
Поделиться:

Комментарии

  1. Программист

    Программист

    Как же хреново был обяснен один момент, цытируууую:» В функции остался цикл и возвращение значение функции:».Сразу же два вопроса:Какая нафиг функция , и где ее взять.Это читать не возможно

  2. npavelFax

    Официальная работа в интернете с обучением.

  3. 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 тоже!
    и даже у вас в примере на следующей странице всё по-другому(правильно)
    почему не изменено здесь?

    чего спрашиваю-то- вы ж меня в ступор на час загнали с этим примером!

Оставить комментарий

Вы должны войти, чтобы оставить комментарий.