Я пытаюсь добавить значок в пункт меню в приложении 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, значок по-прежнему не отображается.





HICON и HBITMAP — это совершенно разные типы. Вы не можете использовать HICON напрямую с SetMenuItemInfo() или SetMenuItemBitmaps().
Вам придется либо:
измените тип ресурса на BITMAP, а затем используйте LoadBitmap() или LoadImage(IMAGE_BITMAP), чтобы загрузить его как HBITMAP.
оставайтесь с ресурсом ICON, загрузите его как HICON, как есть, а затем либо:
а. скопируйте его в отдельный HBITMAP, который затем можно будет назначить пункту меню. См. Как преобразовать HICON в HBITMAP в VC++?
б. Владелец нарисуйте пункт меню , обрабатывая сообщение WM_DRAWITEM , чтобы вы могли нарисовать свой HICON на HDC меню с помощью DrawIcon().
Разве вам не нужны растровые изображения PARGB32 для прозрачности?
Если вам нужна альфа-прозрачность (попиксельная), вам понадобится растровое изображение с разрешением 32 бита на пиксель (V5) в качестве источника и передайте флаг LR_CREATEDIBSECTION в LoadImage().
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, и моя память нечеткая, но я думаю, что здесь происходит пара вещей.
Если вам нужен значок рядом со строкой, вам нужно MIIM_CHECKMARKS и установить поля hbmpChecked и hbmpUnchecked. Когда вы используете MIIM_BITMAP, это означает, что вам нужен значок вместо строки.
Вам нужно использовать настоящие растровые изображения, а не значки. (В WinAPI есть места, где растровые изображения, значки и курсоры могут использоваться взаимозаменяемо, но я не думаю, что это одно из них.)
Пока вы не заработаете, я бы предложил выполнить этот код в отладчике и проверить все возвращаемые значения.
При переходе от значков к растровым изображениям вам понадобятся прозрачные разделы, отображающие цвет меню.
Старый способ сделать это заключался в использовании цветных растровых изображений с 8 или менее битами и загрузке их с помощью LoadImage (с использованием LR_LOADTRANSPARENT и, возможно, LR_LOADMAP3DCOLORS), а не LoadIcon. Цвет «фона» в растровом изображении переназначается на выбранную цветовую схему для трехмерной поверхности, которая обычно совпадает с цветом фона меню.
Как показали другие ответы, вместо этого вы можете использовать 32-битные цветные растровые изображения с альфа-каналом. Детям в наши дни это так легко.
«Чтобы растровые изображения имели «прозрачные» разделы, показывающие цвет меню, вам необходимо использовать растровые изображения с 8-битным цветом» — я уверен, что когда-то, десятилетия назад, это было правдой. С тех пор, как в формате растрового файла появилась поддержка выделенного альфа-канала, нет причин имитировать прозрачность с помощью флага LR_LOADTRANSPARENT. Просто используйте настоящее растровое изображение ARGB с разрешением 32 бита на пиксель (как показано в этом ответе).
@IInspectable: Интересно. Я знаю, что панели инструментов работают с альфа-каналами, но я не знал (или забыл), что стандартные меню были обновлены с этой возможностью.
Вы наблюдаете за возвращаемым значением
LoadIcon(), но затем внезапно перестаете интересоваться тем, что возвращаетSetMenuItemInfo(). Это неразумно.