Архив метки: C++



WinAPI: Динамическое меню

Не знаю, как правильно будет назвать данную тему. Суть в том, что можно создавать меню «на лету». Этому посвящена вторая часть статьи про создание меню для приложения. В прошлый раз мы всё делали через файлы ресурсов.
Поскольку мы не будем использовать файлы ресурсов, в начало файла menu_two.cpp добавим идентификаторы сразу после директивы #include:

#define ID_FILE_EXIT 9001
#define ID_STUFF_GO 9002

Если мы добавим в файл такую строку:

hIcon = LoadImage(NULL, L"menu_two.ico", IMAGE_ICON, 32, 32, LR_LOADFROMFILE);

То получим сообщение об ошибке:

a value of type «HANDLE» cannot be assigned to an entity of type «HICON»

 

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

hIcon = (HICON) LoadImage(NULL, L"menu_two.ico", IMAGE_ICON, 32, 32, LR_LOADFROMFILE);

В случае динамического меню нам уже не потребуются эти строчки, поэтому их можно удалить:

 wc.lpszMenuName = MAKEINTRESOURCE(IDR_MYMENU);
 wc.hIcon = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_MYICON));
 wc.hIconSm = (HICON)LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_MYICON), IMAGE_ICON, 16, 16, 0);

Если удалить старую папку .VSCode, в которой была задача сборки проекта с файлом ресурсов, мы сможем пересобрать наш проект без учёта файла ресурсов. Правда в этот раз меня ждало другое сообщение об ошибке. Оно появилось когда я попытался запустить скомпилированный файл:

Window Registration Failed!

 

Что ж, эту ошибку оказалось не так просто устранить, т.к. здесь нам не было указано, в чём именно проблема. Для начала зададим явный размер структуры WNDCLASSEX:

WNDCLASSEX wc = {0};

Уже лучше — мы получили новое сообщение об ошибке, на этот раз что-то конкретное:

Could not load large icon!

 

Следом за ней ещё одно:

Could not load small icon!

 

Правда на этот раз программа всё же запустится и можно будет открывать меню.

И эти ошибки исправить довольно просто — нам нужно, чтобы файл «menu_two.ico» находился в той же папке, что и запускной файл.
Таким образом полный текст файла menu_two.cpp будет таким:

#define UNICODE
#pragma comment(lib, "user32.lib")

#include <windows.h>

#define ID_FILE_EXIT 9001
#define ID_STUFF_GO 9002

const wchar_t g_szClassName[] = L"myWindowClass";

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
  switch (msg)
  {
  case WM_CREATE:
  {
    HMENU hMenu, hSubMenu;
    HICON hIcon, hIconSm;

    hMenu = CreateMenu();

    hSubMenu = CreatePopupMenu();
    AppendMenu(hSubMenu, MF_STRING, ID_FILE_EXIT, L"E&xit");
    AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT)hSubMenu, L"&File");

    hSubMenu = CreatePopupMenu();
    AppendMenu(hSubMenu, MF_STRING, ID_STUFF_GO, L"&Go");
    AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT)hSubMenu, L"&Stuff");

    SetMenu(hwnd, hMenu);

    hIcon = (HICON)LoadImage(NULL, L"menu_two.ico", IMAGE_ICON, 32, 32, LR_LOADFROMFILE);
    if (hIcon)
      SendMessage(hwnd, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
    else
      MessageBox(hwnd, L"Could not load large icon!", L"Error", MB_OK | MB_ICONERROR);

    hIconSm = (HICON)LoadImage(NULL, L"menu_two.ico", IMAGE_ICON, 16, 16, LR_LOADFROMFILE);
    if (hIconSm)
      SendMessage(hwnd, WM_SETICON, ICON_SMALL, (LPARAM)hIconSm);
    else
      MessageBox(hwnd, L"Could not load small icon!", L"Error", MB_OK | MB_ICONERROR);
  }
  break;
  case WM_LBUTTONDOWN:
  {
    wchar_t szFileName[MAX_PATH];
    HINSTANCE hInstance = GetModuleHandle(NULL);

    GetModuleFileName(hInstance, szFileName, MAX_PATH);
    MessageBox(hwnd, szFileName, L"This program is:", MB_OK | MB_ICONINFORMATION);
  }
  break;
  case WM_CLOSE:
    DestroyWindow(hwnd);
    break;
  case WM_DESTROY:
    PostQuitMessage(0);
    break;
  case WM_COMMAND:
    switch (LOWORD(wParam))
    {
    case ID_FILE_EXIT:
      PostMessage(hwnd, WM_CLOSE, 0, 0);
      break;
    case ID_STUFF_GO:

      break;
    }
    break;
  default:
    return DefWindowProc(hwnd, msg, wParam, lParam);
  }
  return 0;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
  WNDCLASSEX wc = {0};
  HWND hwnd;
  MSG Msg;

  wc.cbSize = sizeof(WNDCLASSEX);
  wc.style = 0;
  wc.lpfnWndProc = WndProc;
  wc.cbClsExtra = 0;
  wc.cbWndExtra = 0;
  wc.hInstance = hInstance;
  wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
  wc.lpszMenuName = NULL;
  wc.lpszClassName = g_szClassName;

  if (!RegisterClassEx(&wc))
  {
    MessageBox(NULL, L"Window Registration Failed!", L"Error!", MB_ICONEXCLAMATION | MB_OK);
    return 0;
  }

  hwnd = CreateWindowEx(
      WS_EX_CLIENTEDGE,
      g_szClassName,
      L"The title of my window",
      WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, CW_USEDEFAULT, 240, 120,
      NULL, NULL, hInstance, NULL);

  if (hwnd == NULL)
  {
    MessageBox(NULL, L"Window Creation Failed!", L"Error!", MB_ICONEXCLAMATION | MB_OK);
    return 0;
  }

  ShowWindow(hwnd, nCmdShow);
  UpdateWindow(hwnd);

  while (GetMessage(&Msg, NULL, 0, 0) > 0)
  {
    TranslateMessage(&Msg);
    DispatchMessage(&Msg);
  }
  return Msg.wParam;
}
Сейчас наша программа подгружает значок программы из внешнего файла!