Как создать пункты меню Win32 API со значками?

Я пытаюсь добавить значок в пункт меню в приложении Windows с помощью API Win32, но значок не отображается. Я выполнил инструкции из документации Win32 API, но он все еще не работает. Может ли кто-нибудь помочь мне решить эту проблему?

HMENU createContextMenu(HWND hwnd, HINSTANCE hInstance) {
    HMENU hMenu = CreatePopupMenu();
    if (hMenu) {
        AppendMenu(hMenu, MF_STRING, IDM_APP_ABOUT, "&About");
        AppendMenu(hMenu, MF_SEPARATOR, (UINT_PTR)-1, NULL);
        AppendMenu(hMenu, MF_STRING, IDM_APP_EXIT, "&Exit");

        // Assuming you have an icon resource named ICON_MENU_EXIT
        HICON hIconExit = LoadIcon(hInstance, MAKEINTRESOURCE(ICON_MENU_EXIT));
        if (hIconExit) {
            MENUITEMINFO menuItemInfo = { sizeof(MENUITEMINFO) };
            menuItemInfo.fMask = MIIM_BITMAP;
            menuItemInfo.hbmpItem = (HBITMAP)CopyImage(hIconExit, IMAGE_ICON, 0, 0, LR_COPYFROMRESOURCE);
            SetMenuItemInfo(hMenu, IDM_APP_EXIT, FALSE, &menuItemInfo);
            DestroyIcon(hIconExit); // Destroy the icon only if setting the image was successful
        }
    }
    return hMenu;
}
case WM_USER_TRAYICON:
        if (lParam == WM_LBUTTONDBLCLK) {
            ShowWindow(hwnd, SW_SHOW);
        } else if (lParam == WM_RBUTTONUP) {
            POINT trayPos;
            GetCursorPos(&trayPos);
            HMENU hMenu = createContextMenu(hwnd,hInstance);
            SetForegroundWindow(hwnd); // Menjamin menu bekerja dengan benar
            UINT clicked = TrackPopupMenu(hMenu, TPM_RETURNCMD | TPM_NONOTIFY, trayPos.x, trayPos.y, 0, hwnd, NULL);
            if (clicked == IDM_APP_EXIT) {
                PostQuitMessage(0);
            }
            DestroyMenu(hMenu); // Hancurkan menu setelah digunakan
        }
        break;

Я попытался загрузить значок из ресурса с помощью функции LoadIcon и установить его в пункт меню с помощью SetMenuItemInfo или SetMenuItemBitmaps.
Я ожидал, что значок появится рядом с пунктом меню «Выход», когда меню отображается.
Однако, несмотря на выполнение шагов, описанных в документации Win32 API, значок по-прежнему не отображается.

Вы наблюдаете за возвращаемым значением LoadIcon(), но затем внезапно перестаете интересоваться тем, что возвращает SetMenuItemInfo(). Это неразумно.

IInspectable 04.06.2024 21:36
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
1
100
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

HICON и HBITMAP — это совершенно разные типы. Вы не можете использовать HICON напрямую с SetMenuItemInfo() или SetMenuItemBitmaps().

Вам придется либо:

  1. измените тип ресурса на BITMAP, а затем используйте LoadBitmap() или LoadImage(IMAGE_BITMAP), чтобы загрузить его как HBITMAP.

  2. оставайтесь с ресурсом ICON, загрузите его как HICON, как есть, а затем либо:

    а. скопируйте его в отдельный HBITMAP, который затем можно будет назначить пункту меню. См. Как преобразовать HICON в HBITMAP в VC++?

    б. Владелец нарисуйте пункт меню , обрабатывая сообщение WM_DRAWITEM , чтобы вы могли нарисовать свой HICON на HDC меню с помощью DrawIcon().

Разве вам не нужны растровые изображения PARGB32 для прозрачности?

David Heffernan 05.06.2024 01:01

Если вам нужна альфа-прозрачность (попиксельная), вам понадобится растровое изображение с разрешением 32 бита на пиксель (V5) в качестве источника и передайте флаг LR_CREATEDIBSECTION в LoadImage().

IInspectable 05.06.2024 11:42
HMENU createContextMenu(HWND hwnd, HINSTANCE hInstance) {
HMENU hMenu = CreatePopupMenu();
if (hMenu) {
    AppendMenu(hMenu, MF_STRING, IDM_APP_ABOUT, "&About");
    AppendMenu(hMenu, MF_SEPARATOR, (UINT_PTR)-1, NULL);
    AppendMenu(hMenu, MF_STRING, IDM_APP_EXIT, "&Exit");

    HICON hMenuExitIcon = LoadIcon(hInstance, MAKEINTRESOURCE(ICON_MENU_EXIT));
    if (hMenuExitIcon) {
        // Create a compatible DC
        HDC hdc = GetDC(hwnd);
        HDC hdcMem = CreateCompatibleDC(hdc);

        // Get the size of the icon
        ICONINFO iconInfo;
        GetIconInfo(hMenuExitIcon, &iconInfo);
        BITMAP bmp;
        GetObject(iconInfo.hbmColor, sizeof(BITMAP), &bmp);

        // Create a bitmap compatible with the DC
        HBITMAP hBitmap = CreateCompatibleBitmap(hdc, bmp.bmWidth, bmp.bmHeight);

        // Select the bitmap into the memory DC
        HBITMAP hOldBitmap = SelectObject(hdcMem, hBitmap);

        // Draw the icon onto the bitmap
        DrawIconEx(hdcMem, 0, 0, hMenuExitIcon, bmp.bmWidth, bmp.bmHeight, 0, NULL, DI_NORMAL);

        // Set the bitmap as the item's bitmap
        MENUITEMINFO menuItemInfo = { sizeof(MENUITEMINFO) };
        menuItemInfo.fMask = MIIM_BITMAP;
        menuItemInfo.hbmpItem = hBitmap;
        SetMenuItemInfo(hMenu, IDM_APP_EXIT, FALSE, &menuItemInfo);

        // Clean up
        SelectObject(hdcMem, hOldBitmap);
        DeleteDC(hdcMem);
        ReleaseDC(hwnd, hdc);
        DestroyIcon(hMenuExitIcon);
    } else {
        MessageBox(NULL, "Failed to load icon for exit menu item.", "Error", MB_OK | MB_ICONERROR);
    }
}
return hMenu;

Теперь это работает

Это старые, запутанные API, и моя память нечеткая, но я думаю, что здесь происходит пара вещей.

  1. Если вам нужен значок рядом со строкой, вам нужно MIIM_CHECKMARKS и установить поля hbmpChecked и hbmpUnchecked. Когда вы используете MIIM_BITMAP, это означает, что вам нужен значок вместо строки.

  2. Вам нужно использовать настоящие растровые изображения, а не значки. (В WinAPI есть места, где растровые изображения, значки и курсоры могут использоваться взаимозаменяемо, но я не думаю, что это одно из них.)

Пока вы не заработаете, я бы предложил выполнить этот код в отладчике и проверить все возвращаемые значения.


При переходе от значков к растровым изображениям вам понадобятся прозрачные разделы, отображающие цвет меню.

  • Старый способ сделать это заключался в использовании цветных растровых изображений с 8 или менее битами и загрузке их с помощью LoadImage (с использованием LR_LOADTRANSPARENT и, возможно, LR_LOADMAP3DCOLORS), а не LoadIcon. Цвет «фона» в растровом изображении переназначается на выбранную цветовую схему для трехмерной поверхности, которая обычно совпадает с цветом фона меню.

  • Как показали другие ответы, вместо этого вы можете использовать 32-битные цветные растровые изображения с альфа-каналом. Детям в наши дни это так легко.

«Чтобы растровые изображения имели «прозрачные» разделы, показывающие цвет меню, вам необходимо использовать растровые изображения с 8-битным цветом» — я уверен, что когда-то, десятилетия назад, это было правдой. С тех пор, как в формате растрового файла появилась поддержка выделенного альфа-канала, нет причин имитировать прозрачность с помощью флага LR_LOADTRANSPARENT. Просто используйте настоящее растровое изображение ARGB с разрешением 32 бита на пиксель (как показано в этом ответе).

IInspectable 06.06.2024 09:56

@IInspectable: Интересно. Я знаю, что панели инструментов работают с альфа-каналами, но я не знал (или забыл), что стандартные меню были обновлены с этой возможностью.

Adrian McCarthy 06.06.2024 15:04

Другие вопросы по теме