Архив метки: VSCode



WinAPI: Диалоговые окна

Итак, мы добрались до следующего урока: Dialogs, GUI coders best friend. Здесь мы пытаемся модифицировать наше окошко с меню так, чтобы в меню Help нам выводилась какая-то информация. Добавим к файлу dlg_one.rc новые строчки:

#include "resource.h"
#include <windows.h>

IDR_MYMENU MENU
BEGIN
    POPUP "&File"
    BEGIN
        MENUITEM "E&xit", ID_FILE_EXIT
    END

    POPUP "&Stuff"
    BEGIN
        MENUITEM "&Go", ID_STUFF_GO
        MENUITEM "G&o somewhere else", 0, GRAYED
    END
END

IDD_ABOUT DIALOG DISCARDABLE  0, 0, 239, 66
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "My About Box"
FONT 8, "MS Sans Serif"
BEGIN
    DEFPUSHBUTTON   "&OK",IDOK,174,18,50,14
    PUSHBUTTON      "&Cancel",IDCANCEL,174,35,50,14
    GROUPBOX        "About this program...",IDC_STATIC,7,7,225,52
    CTEXT           "An example program showing how to use Dialog Boxes\r\n\r\nby theForger",
                    IDC_STATIC,16,18,144,33
END

IDI_MYICON ICON "dlg_one.ico"
Вообще я просто скопировал все файлы из папки старого проекта в новую и переименовал наш старый файл menu_one.rc на dlg_one.rc, а файл с основным кодом menu_one.cpp на dlg_one.cpp.

 

Ошибка RC2104

Но, если оставить фрагмент кода IDD_ABOUT DIALOG без изменений, то при компиляции получим ошибку:

dlg_one.rc(18) : error RC2104 : undefined keyword or key name: DS_MODALFRAME

 

Нужное нам определение содержится в файле WinUser.h:

C:\Program Files (x86)\Windows Kits\10\Include\10.0.19041.0\um\WinUser.h

Можно было бы дополнить файл resource.h такой строчкой:

#ifndef DS_MODALFRAME
  #include <WinUser.h>
#endif
Честно говоря, эти строчки можно и не добавлять. Достаточно в файл dlg_one.rc добавить такую строчку:

#include <windows.h>

 

Но увы, пока это не поможет. Сам файл находится в этой папке:

C:\Program Files (x86)\Windows Kits\10\Include\10.0.19041.0\um

Компилятор ресурсов rc.exe не сможет найти нужный файл:

resource.h(8) : fatal error RC1015: cannot open include file ‘WinUser.h’.

 

Он его не найдёт даже в том случае, если мы добавим путь в переменную окружения PATH. Чтобы выйти из этого затруднительного положения, придётся создать новую переменную окружения, под названием include. Замечу, что мы создадим переменную окружения для текущего пользователя.

C:\Program Files (x86)\Windows Kits\10\Include\10.0.19041.0\um

Чтобы VSCode «увидел» изменения, нужно его перезапустить (не обязательно завершать сеанс пользователя или перезагружать компьютер)!

 

Ошибка RC1015

Хорошо, мы добавили этот путь в новую переменную окружения, но получили новую ошибку:

fatal error RC1015: cannot open include file ‘winapifamily.h’

 

Этот файл находится в другой папке:

C:\Program Files (x86)\Windows Kits\10\Include\10.0.19041.0\shared

А вот здесь есть маленькая проблема, т.к. если мы нажмём кнопку Edit для редактирования данной переменной, то мы увидем такое же окошко, как и при создании переменной, т.е. мы в данный момент НЕ сможем добавить ещё одно значение к переменной include. Хотя… Сможем, но для этого нужно будет сначала дописать к старому значению символ ; , а потом к нему добавить новое значение пути. Получится так:

Ещё одна ошибка RC1015

Внеся изменения в переменную окружения include и перезапустив VSCode, мы получаем новую ошибку компилятора ресурсов:

C:\Program Files (x86)\Windows Kits\10\Include\10.0.19041.0\um\WinUser.h(58) : fatal error RC1015: cannot open include file ‘stdarg.h’.

 

С подобным мы уже сталкивались, поэтому сейчас просто добавляем к переменной ещё одно значение:

C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.28.29910\include

На этот раз диалог редактирования переменной изменился. Благодаря тому, что мы указывали значения через точку с запятой, система понимает, что данная переменная является массивом и, для добавления нового значения теперь появилась кнопка New:

4. К сожалению, ошибки полностью не устранились. На этот раз мы получаем такую ошибку:

C:\Program Files (x86)\Windows Kits\10\Include\10.0.19041.0\um\winnt.h(34) : fatal error RC1015: cannot open include file ‘ctype.h’.

 

Добавляем новый путь к переменной окружения include, после чего перезапускаем VSCode:

C:\Program Files (x86)\Windows Kits\10\Include\10.0.19041.0\ucrt

5. Ещё одна ошибка:

dlg_one.rc(24) : error RC2104 : undefined keyword or key name: IDC_STATIC

 

Данное ключевое слово описано в файле afxres.h, который относится к MFC. Но я не устанавливал эти библиотеки, поэтому попробую обойтись без этого. Дополню файл resource.h:

#ifdef IDC_STATIC
  #undef IDC_STATIC
#endif
#define IDC_STATIC              (-1)     // all static controls

Функция обратного вызова Callback

Функция Callback нам уже встречалась в наших ранних примерах, но я на ней не заострял внимание, т.к. занимался исправлением ошибок компилятора. Сейчас мы добрались до того момента, когда ей нужно уделить более пристальное внимание. Итак, добавив файл dlg_one.rc описанием диалогового окна, нам необходимо описать Процедуру Диалога (Dialog Procedure) для обработки событий в диалоговом окне. Здесь ничего нового и всё практически идентично нашему раннему проекту окошка с меню, но не во всём.

BOOL CALLBACK AboutDlgProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
  switch (Message)
  {
  case WM_INITDIALOG:
    return TRUE;
  case WM_COMMAND:
    switch (LOWORD(wParam))
    {
    case IDOK:
      EndDialog(hwnd, IDOK);
      break;
    case IDCANCEL:
      EndDialog(hwnd, IDCANCEL);
      break;
    }
    break;
  default:
    return FALSE;
  }
  return TRUE;
}

Между Процедурой Диалога и Процедурой Окна есть несколько важных отличий. Во-первых мы НЕ вызываем дефолтовую Процедуру Окна DefWindowProc() для обработки сообщений, которые не обработаны вручную. С Диалогами это делается автоматически (если это сделать, можно серьёзно налажать).
Во-вторых, вы возвращаете значение FALSE для сообщений, которые не обрабатываются, но только до тех пор, пока нужно вернуть какое-то иное значение. В примере выше это видно.
В-третьих, мы не вызываем уничтожение окна DestroyWindow(), чтобы закрыть диалоговое окно. Мы вызываем EndDialog(). Во втором параметре передаётся значение, которое возвращается в код программы, вызвавшей DialogBox().
Наконец, вместо того, чтобы обрабатывать WM_CREATE, мы обрабатываем WM_INITDIALOG, где происходит описание всего необходимого перед тем, как появится окошко диалога, и затем возвращается TRUE для того, чтобы передать фокус для управления с клавиатуры. (Вообще-то также можно обрабатывать и WM_CREATE, но у вас не будет доступа к элементам управления, т.к. он создаётся раньше их, а в WM_INITDIALOG они уже будут созданы).

Здесь мне пришлось опять поковыряться, т.к. вывалились ошибки связанные с необъявленными идентификаторами. Для их устранения, необходимо добавить в файл resource.h следующие строчки:

#define ID_HELP_ABOUT 9002
#define IDD_ABOUT   100

Правда мы получим новую ошибку:

argument of type «BOOL (__stdcall *)(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)» is incompatible with parameter of type «DLGPROC»

 

В файле WinUser.h объявлено, что функция DialogBoxParamW должна возвращать значение типа INT_PTR:

Ну, либо можно посмотреть на MSDN, там то же самое.
Таким образом, данная ошибка устраняется, если объявить CALLBACK-функцию AboutDlgProc по другому:

INT_PTR CALLBACK AboutDlgProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)

Ошибки


В оригинальной статье этого раздела нет. Там всё здорово. Там и многих других «букв» тоже небыло, т.к. не было ошибок при компиляции. Тот код компилировали как C-код, а не С++. Там был другой компилятор и, явно другоя операционная система (максимум Windows 7, причём не 64 бита). В итоге, даже устранив все ошибки компиляции, я всё равно не могу корректно запусть программу. Сейчас ошибку выдаёт меню StuffGo:

Казалось бы всё правильно, мы всё скопировали согласно той статье. Ну да, только автор почему-то забыл указать, что мы создали новый файл из старого.
В новом проекте у нас в меню теперь StuffGo заменён на HelpAbout! Но код вызывается старый. Т.е. у нас нет нового кода, отсюда и ошибка.
Исправленный и полный код файла dlg_one.rc будет таким:

#include "resource.h"
#include <windows.h>

IDR_MYMENU MENU
BEGIN
    POPUP "&File"
    BEGIN
        MENUITEM "E&xit", ID_FILE_EXIT
    END

    POPUP "&Help"
    BEGIN
        MENUITEM "&About", ID_HELP_ABOUT
        MENUITEM "G&o somewhere else", 0, GRAYED
    END
END

IDD_ABOUT DIALOG DISCARDABLE  0, 0, 239, 66
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "My About Box"
FONT 8, "MS Sans Serif"
BEGIN
    DEFPUSHBUTTON   "&OK",IDOK,174,18,50,14
    PUSHBUTTON      "&Cancel",IDCANCEL,174,35,50,14
    GROUPBOX        "About this program...",IDC_STATIC,7,7,225,52
    CTEXT           "An example program showing how to use Dialog Boxes\r\n\r\nby theForger",
                    IDC_STATIC,16,18,144,33
END

IDI_MYICON ICON "dlg_one.ico"

Ну вот, вроде всё разгрёб :)