cindywmiao
cindywmiao

Reputation: 947

In C++ Win32 Program, How to modifier the menu's title?

I created a popup menu using c++ win32. The source code is:

IDR_POPUP_MENU MENU DISCARDABLE
BEGIN
POPUP "POPUP"
BEGIN
    MENUITEM "Turn On"                          IDM_STATUS
    POPUP "Preferences"         
    BEGIN
        MENUITEM "Turn Autostart Off"                           IDM_AUTOSTART 
        MENUITEM "Turn Lock Screen Off"                         IDM_LOCKSCREEN
    END
    MENUITEM "Exit",                        IDM_EXIT
END
END

Now I want to change the title "Turn LockScreen Off" into "Turn LockScreen On" when I clicked it.

The code in .cpp is:

case IDM_LOCKSCREEN:
{
    if(lockscreen)
    {
        lockscreen = FALSE;
        HMENU hMenu = LoadMenu(NULL, MAKEINTRESOURCE(IDR_POPUP_MENU));
        hMenu = GetSubMenu(hMenu, IDM_LOCKSCREEN);
        ModifyMenu(hMenu, IDM_LOCKSCREEN, MF_BYCOMMAND | MF_CHECKED, IDM_LOCKSCREEN, "Turn LockScreen Off");
    }

    break;
}

But I could not get the hMenu with this method.

The pointer of hMenu is empty. I put a break point there and get (*hMenu).unused:CXX0030:Error: expression cannot be evaluated!

Could somebody explain to me how to do with this error?

Upvotes: 5

Views: 3013

Answers (2)

dan_din_pantelimon
dan_din_pantelimon

Reputation: 101

The following code will load your popup menu, and modify the text of the IDM_LOCKSCREEN menu item

HMENU popup_menu_1  = ::LoadMenu(::GetModuleHandle(nullptr),  MAKEINTRESOURCE(IDR_POPUP_MENU));
HMENU sub_menu_1    = ::GetSubMenu(popup_menu_1, 0);
HMENU popup_menu_2  = ::GetSubMenu(sub_menu_1, 1);
HMENU sub_menu_2    = ::GetSubMenu(popup_menu_2, 0);
::ModifyMenu(sub_menu_2, IDM_LOCKSCREEN, MF_BYCOMMAND | MF_STRING, IDM_LOCKSCREEN, L"Turn LockScreen ON");   

I would do that just before displaying the menu:

HMENU popup_menu_1  = ::LoadMenu(::GetModuleHandle(nullptr),  MAKEINTRESOURCE(IDR_MENU1));
HMENU sub_menu_1    = ::GetSubMenu(popup_menu_1, 0);
HMENU popup_menu_2  = ::GetSubMenu(sub_menu_1, 1);
HMENU sub_menu_2    = ::GetSubMenu(popup_menu_2, 0);
::ModifyMenu(sub_menu_2, IDM_LOCKSCREEN, MF_BYCOMMAND | MF_STRING, IDM_LOCKSCREEN, L"Turn LockScreen ON");   

::ClientToScreen(hwnd, &point);  // maybe you need this
::TrackPopupMenu(sub_menu_1, TPM_LEFTALIGN | TPM_LEFTBUTTON, point.x, point.y, 0, hwnd, nullptr);

Where hwnd is the handle of the popup menu's parent window and point the location where the popup menu should be displayed.

Upvotes: 0

Brandon
Brandon

Reputation: 23515

The reason your code fails is because you are replacing hMenu with GetSubMenu. Your menu should have been loaded at the beginning within the WM_CREATE case. The menu you are modifying is the recently loaded menu and it is NOT added to the current window so you aren't seeing any changes.. For ModifyMenu, you NEED to specify the MF_STRING flag to change the title!

If the menu is dynamic and created upon Right-Click, then you don't need to use SetMenu to set the menu and it can be loaded on the fly and changed.. but remember, you do not need GetSubMenu for this because you are using defined ID's for each menu. If the menu is to be retrieved by indexed position starting at 0, GetSubMenu would help.

Also, in the below code, I chose to save the menu handle into a variable. You do not need this static variable. You can also use GetMenu within the case statement that needs a MenuHandle.

Resources.h:

#ifndef RESOURCES_HPP_INCLUDED
#define RESOURCES_H_INCLUDED

#define IDR_POPUP_MENU 1001
#define IDM_STATUS 1002
#define IDM_AUTOSTART 1003
#define IDM_LOCKSCREEN 1004
#define IDM_EXIT 1005

#endif // RESOURCES_H_INCLUDED

Resources.rc:

#include "Resources.h"

IDR_POPUP_MENU MENU DISCARDABLE
BEGIN
POPUP "POPUP"
BEGIN
    MENUITEM "Turn On", IDM_STATUS
    POPUP "Preferences"
    BEGIN
        MENUITEM "Turn Autostart Off", IDM_AUTOSTART
        MENUITEM "Turn Lock Screen Off", IDM_LOCKSCREEN
    END
    MENUITEM "Exit", IDM_EXIT
END
END

main.cpp:

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

LRESULT CALLBACK WindowProcedure(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    static HMENU hMenu = NULL;

    switch(message)
    {
        case WM_CREATE:
        {
            hMenu = LoadMenu(NULL, MAKEINTRESOURCE(IDR_POPUP_MENU));
            SetMenu(hwnd, hMenu);

        }
        break;

        case WM_COMMAND:
        {
            switch(wParam)
            {
                case IDM_LOCKSCREEN:
                {
                    static bool locked = true;

                    if (locked)
                    {
                        //You can use HMENU = GetMenu(hwnd); if you want.. instead of a static variable..
                        ModifyMenu(hMenu, IDM_LOCKSCREEN, MF_BYCOMMAND | MF_STRING, IDM_LOCKSCREEN, "Turn LockScreen On");
                    }
                    else
                    {
                        ModifyMenu(hMenu, IDM_LOCKSCREEN, MF_BYCOMMAND | MF_STRING | MF_CHECKED, IDM_LOCKSCREEN, "Turn LockScreen Off");
                    }

                    locked = !locked;
                }
                break;
            }
        }
        break;

        case WM_DESTROY:
            PostQuitMessage(0);
            break;
        default:
            return DefWindowProc (hwnd, message, wParam, lParam);
    }

    return 0;
}

int WINAPI WinMain(HINSTANCE hThisInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int nCmdShow)
{
    HWND hwnd;
    MSG messages;

    WNDCLASSEX wincl;
    wincl.hInstance = hThisInstance;
    wincl.lpszClassName = "CodeBlocksWindowsApp";
    wincl.lpfnWndProc = WindowProcedure;
    wincl.style = CS_DBLCLKS;
    wincl.cbSize = sizeof (WNDCLASSEX);
    wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
    wincl.lpszMenuName = NULL;
    wincl.cbClsExtra = 0;
    wincl.cbWndExtra = 0;
    wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND;

    if (!RegisterClassEx (&wincl))
        return 0;

    hwnd = CreateWindowEx (0, "CodeBlocksWindowsApp", "Code::Blocks Template Windows App", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 544, 375, HWND_DESKTOP, NULL, hThisInstance, NULL);
    ShowWindow (hwnd, nCmdShow);

    while (GetMessage (&messages, NULL, 0, 0))
    {
        TranslateMessage(&messages);
        DispatchMessage(&messages);
    }
    return messages.wParam;
}

Upvotes: 1

Related Questions