i'm using the HiliteMenuItem() for test if the mouse is above the menu item:

int i=0;
                        for(i=0; i<GetMenuItemCount(menuhandle); i++)
                        {
                            if(HiliteMenuItem(HandleWindow,menuhandle,i,MF_BYPOSITION) == true)
                            {
                                menuposition=(UINT)i;
                                break;
                            }
                        }

but i always get the number 0. heres how i detect the menu handle:

case WM_INITMENUPOPUP:
                {
                    menuhandle=(HMENU)wParam;
                    return 0;
                }
                break;

what i'm doing wrong?

i'm trying several ways without sucess :(

case WM_INITMENUPOPUP:
                {
                    menuhandle=(HMENU)wParam;
                    return 0;
                }
                break;

                case WM_SYSCOMMAND:
                {
                    if(wParam & SC_MOUSEMENU)
                    {
                        MENUITEMINFO menuInfo;
                        menuInfo.cbSize = sizeof(MENUITEMINFO);
                        menuInfo.fMask=MIIM_DATA;
                        int xPos = GET_X_LPARAM(lParam);    // horizontal position
                        int yPos = GET_Y_LPARAM(lParam);    // vertical position
                        menuposition=MenuItemFromPoint(HandleWindow,menuhandle,{xPos,yPos});
                        if(!GetMenuItemInfo(menuhandle,(UINT) menuposition, true, &menuInfo ))
                        {
                            Menu *mMenu = (Menu *) menuInfo.dwItemData;
                            if (mMenu!=NULL)
                            {
                                mMenu->MenuClick();
                            }
                        }
                    }

                    return 0;
                }
                break;

now the control system menu isn't showed when i click on it :(

continue with WM_SYSCOMMAND: how can i test if the menu item was clicked?
(if i open an close the control system menu(without click on menu item), i will get errors)

You should be receiving an event for WM_MENUCOMMAND or WM_COMMAND when the Menu is clicked. If nothing is clicked, you receive nothing.

the system control menu, uses the WM_SYSCOMMAND message. the WM_MENUSELECT and WM_INITMENUPOPUP messages are global i think.
using the WM_SYSCOMMAND and return:

return DefWindowProc(HandleWindow, msg, wParam, lParam);

if i open the control menu and then close it(without click on menu item, just out of the menu), the windows(OS) can give me an error and then close my program :(

Works fine as far as I can tell.. You need to give more details or show code.

#if defined(UNICODE) && !defined(_UNICODE)
#define _UNICODE
#elif defined(_UNICODE) && !defined(UNICODE)
#define UNICODE
#endif

#include <tchar.h>
#include <windows.h>

LRESULT CALLBACK WindowProcedure(HWND, UINT, WPARAM, LPARAM);

TCHAR szClassName[] = _T("CodeBlocksWindowsApp");

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

    WNDCLASSEX wincl;
    wincl.hInstance = hThisInstance;
    wincl.lpszClassName = szClassName;
    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, szClassName, _T("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;
}

LRESULT CALLBACK WindowProcedure(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch(message)
    {
        case WM_CREATE:
        {
            HMENU menu = GetSystemMenu(hwnd, false);
            InsertMenu(menu, 0, MF_BYPOSITION | MF_STRING, 1, "My Custom Menu");
        }
        break;

        case WM_SYSCOMMAND:
        {
            switch(wParam)
            {
                case 1:
                    MessageBox(NULL, "My Custom Menu Clicked", "Notice", 0);
                    break;

                case SC_MINIMIZE:
                    MessageBox(NULL, "Minimise Clicked", "Notice", 0);
                    //break;

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

        case WM_DESTROY:
            PostQuitMessage(0);
            break;

        default:
            return DefWindowProc(hwnd, message, wParam, lParam);
    }

    return 0;
}

Edited 1 Year Ago by triumphost

Comments
your code give me that idea... thanks for all

thanks your code is nice and i understand it ;)
let me ask 1 thing: using WM_SYSCOMMAND what is the wParam that tell me that the any menu was clicked?
unless i take off all the wParam consts for do the job ;)
i see the wParam or is a menu ID or a system action

thanks for that you give me an idea ;)

case WM_SYSCOMMAND:
                {
                    if (menuhandle!=NULL && GetMenuState(menuhandle,wParam,MF_BYCOMMAND)!=-1 )
                    {
                        MENUITEMINFO menuInfo;
                        menuInfo.cbSize = sizeof(MENUITEMINFO);
                        menuInfo.fMask=MIIM_DATA;

                        GetMenuItemInfo(menuhandle,(UINT) wParam, FALSE, &menuInfo );

                        Menu *mMenu = (Menu *) menuInfo.dwItemData;
                        if (mMenu!=NULL)
                        {
                            mMenu->Click();
                        }
                    }
                    menuhandle=NULL;
                    menuitem=-1;

                    return DefWindowProc(HandleWindow, msg, wParam, lParam);
                }
                break;

i will test more, but i think it's working fine... thanks
thanks for your sample ;)

now i'm trying do the mouse enter and mouse exit commands:

case WM_MENUSELECT:
                {
                    if(menuitem!=-1)//call the mouse exit
                    {
                        MENUITEMINFO menuInfo;
                        menuInfo.cbSize = sizeof(MENUITEMINFO);
                        menuInfo.fMask=MIIM_DATA;

                        GetMenuItemInfo((HMENU)lParam,menuitem, FALSE, &menuInfo );

                        Menu *mMenu = (Menu *) menuInfo.dwItemData;
                        if (mMenu!=NULL)
                        {
                            mMenu->Leave();
                        }
                    }
                    //do the mouse enter
                    menuitem=LOWORD(wParam);
                    MENUITEMINFO menuInfo;
                    menuInfo.cbSize = sizeof(MENUITEMINFO);
                    menuInfo.fMask=MIIM_DATA;

                    GetMenuItemInfo((HMENU)lParam,menuitem, FALSE, &menuInfo );

                    Menu *mMenu = (Menu *) menuInfo.dwItemData;
                    if (mMenu!=NULL)
                    {
                        mMenu->Enter();
                    }

                    return 0;
                }
                break;

but don't works :(
(after a menu item been clicked, the menuitem will be -1)
so what i'm doing wrong?

You're not handling the LOWORD or HIWORD properly.. You seem to be mis-interpretting it.

You cannot just ASSUME that the menu is stored in lParam because that's incorrect. lParam can be NULL (when the menu is closed). lParam can be another menu that is NOT yours.

You need to check the state of WPARAM then check lParam and see if it is your menu. Finally, you can check the state of your menu. An example (I use ID's.. it could be an HMENU depending on your menu setup) is shown below:

#define MENU_ID 0x01   

#include <tchar.h>
#include <windows.h>
#include <iostream>

LRESULT CALLBACK WindowProcedure(HWND, UINT, WPARAM, LPARAM);

TCHAR szClassName[] = _T("CodeBlocksWindowsApp");

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

    WNDCLASSEX wincl;
    wincl.hInstance = hThisInstance;
    wincl.lpszClassName = szClassName;
    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, szClassName, _T("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;
}

LRESULT CALLBACK WindowProcedure(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{    
    switch(message)
    {
        case WM_CREATE:
        {
            HMENU menu = GetSystemMenu(hwnd, false);
            InsertMenu(menu, 0, MF_BYPOSITION | MF_STRING, MENU_ID, "My Custom Menu");
        }
        break;

        case WM_SYSCOMMAND:
        {
            switch(wParam)
            {
                case MENU_ID:
                    MessageBox(NULL, "My Custom Menu Clicked", "Notice", 0);
                    break;

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

        case WM_MENUSELECT:
        {
            static int Last_Menu_ID = -1;

            if(((HIWORD(wParam) & MF_HILITE) || (HIWORD(wParam) & MF_MOUSESELECT)) && LOWORD(wParam) == MENU_ID)
            {
                Last_Menu_ID = LOWORD(wParam);
                std::cout<<"Highlighted My Menu - Mouse Entered.\n";
            }
            else if(HIWORD(wParam) == 0xFFFF && lParam == 0x0000)
            {
                if(Last_Menu_ID != -1)
                {
                    Last_Menu_ID = -1;
                    std::cout<<"My Menu Closed - Mouse Exit.\n";
                }
                else
                {
                    Last_Menu_ID = -1;
                    std::cout<<"ALL Menus Closed\n";
                }
            }
            else
            {
                if(Last_Menu_ID != -1)
                {
                    Last_Menu_ID = -1;
                    std::cout<<"Un-Highlighted My Menu - Mouse Leave.\n";
                }
            }

            return DefWindowProc(hwnd, message, wParam, lParam);
        }
        break;


        case WM_DESTROY:
            PostQuitMessage(0);
            break;

        default:
            return DefWindowProc(hwnd, message, wParam, lParam);
    }

    return 0;
}

Edited 1 Year Ago by triumphost

i think that i miss undertood your code:

case WM_MENUSELECT:
                {
                    static int Last_Menu_ID = -1;
                    if(((HIWORD(wParam) & MF_HILITE) || (HIWORD(wParam) & MF_MOUSESELECT)) && GetMenuState(menuhandle,LOWORD(wParam),MF_BYCOMMAND)!=-1)
                    {
                        //Mouse Enter
                        Last_Menu_ID = LOWORD(wParam);
                        MENUITEMINFO menuInfo;
                        menuInfo.cbSize = sizeof(MENUITEMINFO);
                        menuInfo.fMask=MIIM_DATA;
                        GetMenuItemInfo((HMENU)lParam,Last_Menu_ID, FALSE, &menuInfo );
                        Menu *mMenu = (Menu *) menuInfo.dwItemData;
                        if (mMenu!=NULL)
                        {
                            mMenu->Enter();
                        }
                    }
                    else if(HIWORD(wParam) == 0xFFFF && lParam == 0x0000)
                    {
                        //mouse live the menu
                        if(Last_Menu_ID != -1)
                        {
                            MENUITEMINFO menuInfo;
                            menuInfo.cbSize = sizeof(MENUITEMINFO);
                            menuInfo.fMask=MIIM_DATA;
                            GetMenuItemInfo((HMENU)lParam,Last_Menu_ID, FALSE, &menuInfo );
                            Menu *mMenu = (Menu *) menuInfo.dwItemData;
                            if (mMenu!=NULL)
                            {
                                mMenu->Leave();
                            }
                            Last_Menu_ID = -1;
                        }
                        else
                        {
                            Last_Menu_ID = -1;
                            SetWindowText(HandleWindow,"ALL Menus Closed");
                        }
                    }
                    else
                    {
                        //mouse leave the menu item
                        if(Last_Menu_ID != -1) 
                        {
                            MENUITEMINFO menuInfo;
                            menuInfo.cbSize = sizeof(MENUITEMINFO);
                            menuInfo.fMask=MIIM_DATA;
                            GetMenuItemInfo((HMENU)lParam,Last_Menu_ID, FALSE, &menuInfo );
                            Menu *mMenu = (Menu *) menuInfo.dwItemData;
                            if (mMenu!=NULL)
                            {
                                mMenu->Leave();
                            }
                            Last_Menu_ID = -1;
                            //SetWindowText(HandleWindow, to_string(GetLastError()).c_str());
                        }
                    }
                    return DefWindowProc(HandleWindow, msg, wParam, lParam);
                }
                break;

the mouse leave(menu item) isn't working :(

Here try this.. It's very hard to explain so hopefully you can understand it by seeing the code:

Menu-Class CodeBlocks Example

It works like this:

When you create a menu, you want to set it to MNS_NOTIFYBYPOS. This means you'll get WM_MENUCOMMAND, WM_SYSCOMMAND, WM_INITMENUPOPUP, WM_UNINITMENUPOPUP, WM_MENUSELECT.

WM_MENUCOMMAND will let you know when a menu is "clicked".
WM_SYSCOMMAND will let you know when a system menu is "clicked".
WM_INITMENUPOPUP will let you know when any menu has been "opened".
WM_UNINITMENUPOPUP will let you know when any menu has been "closed".
WM_MENUSELECT will let you know when any menu has been "hovered" over.

Now when creating each menu, I added the "this" pointer to the MenuInfo.dwMenuData.

I also added the "this" pointer to the MenuItemInfo.dwItemData.

That means that every time you get any of the above events, you can read BOTH the MenuInfo.dwMenuData or the MenuItemInfo.dwItemData to get a handle to your class instance..

That is quite wonderful because it becomes quite easy to make Object-Oriented Menus and call EventHandlers on the corresponding menu.

Another thing that I did was generate some ID's for the controls. I did this so that I can store the ID into the menu's MenuItemInfo.wID field. Why? Because if I don't do that, all popup menus will have the "handle" as the "ID". That's not convenient for us because we want to keep all menus consistent.

We use the ID to identify the exact menu rather than by position. We use the HMENU to retrieve our "this" pointer and finally, we call whatever function we want using the "this" pointer.

Now it's just a matter of handling the WinAPI events properly.. You can see how it's handle below.. Along with the code for the menus..

You can either download the code from the above link OR view it below..

Control.h:

#ifndef CONTROLS_H_INCLUDED
#define CONTROLS_H_INCLUDED

#include <windows.h>
#include <vector>
#include <string>


class Control
{
    private:
        int ID;
        void CreateID();

    public:
        Control() : ID(0) {CreateID();}
        virtual ~Control() {}

        int GetID() const {return this->ID;}
        virtual void* GetControlHandle() = 0;
};



class MenuItem;
class MenuBar : public Control
{
    private:
        friend MenuItem;
        bool SystemMenu;
        HWND Parent;
        HMENU Handle;
        std::vector<MenuItem*> Children;

    protected:
        virtual void* GetControlHandle() {return this->Handle;}

    public:
        MenuBar(HWND Parent, bool SystemMenu = false);
        MenuBar(MenuBar&& M);
        virtual ~MenuBar();

        void Show() {SetMenu(this->Parent, Handle);};

        HWND GetParent() const {return this->Parent;}
        HMENU GetHandle() const {return this->Handle;}
        bool IsSystemMenu() const {return this->SystemMenu;}
};



class MenuItem : public Control
{
    private:
        friend MenuBar;
        HMENU Handle;
        bool SystemMenu;
        Control* Parent;
        std::wstring Text;
        DWORD Style;
        std::vector<MenuItem*> Children;

    protected:
        virtual void* GetControlHandle() {return this->Handle;}

    public:
        MenuItem(MenuItem&& M);
        MenuItem(DWORD Style, const wchar_t* Text);
        virtual ~MenuItem();

        MenuItem(MenuBar* M, DWORD Style, const wchar_t* Text);
        MenuItem(MenuItem* M, DWORD Style, const wchar_t* Text);

        void AddItem(MenuItem* M);
        MenuItem* FindMenu(int ID);
        MenuItem* FindMenu(const wchar_t* MenuName);

        bool ToggleCheck();
        bool IsChecked() const;
        const wchar_t* GetText();

        HMENU GetHandle() const {return this->Handle;}
        bool IsSystemMenu() const {return this->SystemMenu;}
};

#endif // CONTROLS_H_INCLUDED

Control.cpp:

#include "Controls.h"


                                    /** Control **/

void Control::CreateID()
{
    static int IDs = 0;
    this->ID = ++IDs;
}

                                    /** MenuBar **/

MenuBar::MenuBar(HWND Parent, bool SystemMenu) : SystemMenu(SystemMenu), Parent(Parent), Handle(SystemMenu ? GetSystemMenu(Parent, false) : CreateMenu()), Children()
{
    if (!SystemMenu)
    {
        MENUINFO MenuInfo = {0};
        MenuInfo.cbSize = sizeof(MenuInfo);
        MenuInfo.fMask = MIM_STYLE | MIM_MENUDATA;
        GetMenuInfo(Handle, &MenuInfo);
        MenuInfo.dwStyle |= MNS_NOTIFYBYPOS;
        MenuInfo.dwMenuData = reinterpret_cast<ULONG_PTR>(this);
        SetMenuInfo(Handle, &MenuInfo);
    }
}

MenuBar::MenuBar(MenuBar&& M) : Control(std::move(M)), Parent(nullptr), Handle(nullptr), Children()
{
    using std::swap;
    swap(M.SystemMenu, this->SystemMenu);
    swap(M.Parent, this->Parent);
    swap(M.Handle, this->Handle);
    swap(M.Children, this->Children);

    for (auto &it : this->Children)
    {
        static_cast<MenuItem*>(it)->Parent = this;
    }
}

MenuBar::~MenuBar()
{
    if (!Parent)
    {
        DestroyMenu(Handle);
        Handle = nullptr;
    }
}


                          /** Menu Item **/


MenuItem::MenuItem(MenuItem&& M) : Control(std::move(M)), Handle(nullptr), SystemMenu(false), Parent(nullptr), Children()
{
    using std::swap;
    swap(M.Handle, this->Handle);
    swap(M.SystemMenu, this->SystemMenu);
    swap(M.Parent, this->Parent);
    swap(M.Text, this->Text);
    swap(M.Style, this->Style);
    swap(M.Children, this->Children);

    for (auto &it : Children)
    {
        it->Parent = this;
    }
}

MenuItem::MenuItem(DWORD Style, const wchar_t* Text) : Control(), Handle(Style & MF_POPUP ? CreatePopupMenu() : CreateMenu()), SystemMenu(false), Parent(nullptr), Text(Text), Style(Style), Children()
{
    MENUINFO MenuInfo = {0};
    MenuInfo.cbSize = sizeof(MenuInfo);
    MenuInfo.fMask = MIM_STYLE | MIM_MENUDATA;
    GetMenuInfo(Handle, &MenuInfo);
    MenuInfo.dwStyle = MenuInfo.dwStyle | MNS_NOTIFYBYPOS;
    MenuInfo.dwMenuData = reinterpret_cast<ULONG_PTR>(this);
    SetMenuInfo(Handle, &MenuInfo);
}

MenuItem::~MenuItem()
{
    if(!Parent)
    {
        DestroyMenu(Handle);
        Handle = nullptr;
    }
}

MenuItem::MenuItem(MenuBar* M, DWORD Style, const wchar_t* Text) : MenuItem(Style | (M->SystemMenu ? 0 : MF_POPUP), Text)
{
    this->Parent = M;
    this->SystemMenu = M->SystemMenu;
    M->Children.emplace_back(this);
    AppendMenuW(M->GetHandle(), static_cast<UINT>(this->Style), reinterpret_cast<UINT_PTR>(Handle), &Text[0]);

    MENUITEMINFOW ItemInfo = {0};
    ItemInfo.cbSize = sizeof(ItemInfo);
    ItemInfo.fMask = MIIM_DATA | MIIM_ID;
    ItemInfo.wID = this->GetID();
    ItemInfo.dwItemData = reinterpret_cast<ULONG_PTR>(this);
    SetMenuItemInfoW(M->GetHandle(), reinterpret_cast<UINT_PTR>(this->Handle), false, &ItemInfo);
}

MenuItem::MenuItem(MenuItem* M, DWORD Style, const wchar_t* Text) : MenuItem(Style, Text)
{
    M->AddItem(this);
}

void MenuItem::AddItem(MenuItem* child)
{
    child->Parent = this;
    child->SystemMenu = this->SystemMenu;
    Children.emplace_back(child);
    AppendMenuW(Handle, static_cast<UINT>(child->Style), reinterpret_cast<UINT_PTR>(child->Handle), child->Text.c_str());

    MENUITEMINFOW ItemInfo = {0};
    ItemInfo.cbSize = sizeof(ItemInfo);
    ItemInfo.fMask = MIIM_DATA | MIIM_ID;
    ItemInfo.wID = child->GetID();
    ItemInfo.dwItemData = reinterpret_cast<ULONG_PTR>(child);
    SetMenuItemInfoW(Handle, reinterpret_cast<UINT_PTR>(child->Handle), false, &ItemInfo);
}

MenuItem* MenuItem::FindMenu(int ID)
{
    for (auto &it : Children)
    {
        if (it->GetID() == ID)
        {
            return it;
        }
    }
    return nullptr;
}

MenuItem* MenuItem::FindMenu(const wchar_t* MenuName)
{
    for (auto &it : Children)
    {
        if (it->Parent == this)
        {
            const wchar_t* text = it->GetText();
            if (text && !wcscmp(MenuName, text))
            {
                return it;
            }
        }
    }
    return nullptr;
}

bool MenuItem::ToggleCheck()
{
    MENUITEMINFOW MenuInfo = {0};
    MenuInfo.cbSize = sizeof(MenuInfo);
    MenuInfo.fMask = MIIM_STATE;
    HMENU parent = reinterpret_cast<HMENU>(this->Parent->GetControlHandle());
    GetMenuItemInfoW(parent, this->GetID(), false, &MenuInfo);
    MenuInfo.fState = (MenuInfo.fState & MF_CHECKED ? MenuInfo.fState & ~MF_CHECKED : MenuInfo.fState | MF_CHECKED);
    SetMenuItemInfoW(parent, this->GetID(), false, &MenuInfo);
    return MenuInfo.fState & MF_CHECKED;
}

bool MenuItem::IsChecked() const
{
    MENUITEMINFOW MenuInfo = {0};
    MenuInfo.cbSize = sizeof(MenuInfo);
    MenuInfo.fMask = MIIM_STATE;
    HMENU parent = reinterpret_cast<HMENU>(this->Parent->GetControlHandle());
    GetMenuItemInfoW(parent, this->GetID(), false, &MenuInfo);
    return MenuInfo.fState & MF_CHECKED;
}

const wchar_t* MenuItem::GetText()
{
    HMENU parent = reinterpret_cast<HMENU>(this->Parent->GetControlHandle());

    MENUITEMINFOW ItemInfo = {0};
    ItemInfo.cbSize = sizeof(ItemInfo);
    ItemInfo.fMask = MIIM_TYPE;
    ItemInfo.dwTypeData = nullptr;

    GetMenuItemInfoW(parent, this->GetID(), false, &ItemInfo);

    if (ItemInfo.dwTypeData == MFT_STRING)
    {
        Text.resize(++ItemInfo.cch);
        ItemInfo.dwTypeData = &Text[0];
        if (GetMenuItemInfoW(parent, this->GetID(), false, &ItemInfo))
        {
            return Text.c_str();
        }
    }
    return nullptr;
}

main.cpp

#include "Controls.h"
#include <iostream>


LRESULT CALLBACK WindowProcedure(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    static std::vector<Control*> menus;

    switch(message)
    {
        case WM_CREATE:
        {
            /** Window Menu **/

            MenuBar* bar = new MenuBar(hwnd);
            MenuItem* file = new MenuItem(bar, MF_STRING, L"File");
            MenuItem* edit = new MenuItem(bar, MF_STRING, L"Edit");
            MenuItem* view = new MenuItem(bar, MF_STRING, L"View");
            MenuItem* about = new MenuItem(bar, MF_STRING, L"About");
            MenuItem* open = new MenuItem(file, MF_STRING | MF_POPUP, L"Open");
            MenuItem* sub = new MenuItem(open, MF_STRING, L"Open->Sub");
            MenuItem* sub2 = new MenuItem(open, MF_STRING, L"Open->Sub2");
            MenuItem* exit = new MenuItem(file, MF_STRING | MF_CHECKED, L"Exit");
            bar->Show();

            menus.push_back(bar);
            menus.push_back(file);
            menus.push_back(edit);
            menus.push_back(view);
            menus.push_back(about);
            menus.push_back(open);
            menus.push_back(sub);
            menus.push_back(sub2);
            menus.push_back(exit);

            /** System Menu **/

            MenuBar* sys_bar = new MenuBar(hwnd, true);
            MenuItem* sys_file = new MenuItem(sys_bar, MF_STRING | MF_POPUP, L"File");
            MenuItem* sys_open = new MenuItem(sys_file, MF_STRING | MF_POPUP, L"Open");
            MenuItem* sys_sub = new MenuItem(sys_open, MF_STRING, L"Open->Sub");
            MenuItem* sys_sub2 = new MenuItem(sys_file, MF_STRING, L"File->Sub");

            menus.push_back(sys_bar);
            menus.push_back(sys_file);
            menus.push_back(sys_open);
            menus.push_back(sys_sub);
            menus.push_back(sys_sub2);
        }
        break;

        case WM_MENUCOMMAND:
        {
            MENUITEMINFOW ItemInfo = {0};
            ItemInfo.cbSize = sizeof(ItemInfo);
            ItemInfo.fMask = MIIM_DATA;
            GetMenuItemInfoW(reinterpret_cast<HMENU>(lParam), wParam, true, &ItemInfo);

            MenuItem* menu = reinterpret_cast<MenuItem*>(ItemInfo.dwItemData);
            if(menu)
            {
                std::wcout<<L"CLICKED: "<<menu->GetText()<<L"\n";
            }
        }
        break;

        case WM_SYSCOMMAND:
        {
            MENUITEMINFOW ItemInfo = {0};
            ItemInfo.cbSize = sizeof(ItemInfo);
            ItemInfo.fMask = MIIM_DATA;
            GetMenuItemInfoW(GetSystemMenu(hwnd, false), wParam, false, &ItemInfo);

            MenuItem* menu = reinterpret_cast<MenuItem*>(ItemInfo.dwItemData);
            if(menu)
            {
                std::wcout<<L"Clicked: "<<menu->GetText()<<L"\n";
            }
            return DefWindowProc(hwnd, message, wParam, lParam);
        }
        break;

        case WM_INITMENUPOPUP:
        {
            if (wParam)
            {
                MENUINFO MenuInfo = {0};
                MenuInfo.cbSize = sizeof(MenuInfo);
                MenuInfo.fMask = MIM_MENUDATA;
                GetMenuInfo(reinterpret_cast<HMENU>(wParam), &MenuInfo);

                MenuItem* menu = reinterpret_cast<MenuItem*>(MenuInfo.dwMenuData);
                if(menu)
                {
                    std::wcout<<L"OPENED: "<<menu->GetText()<<L"\n";
                }
            }
        }
        break;

        case WM_UNINITMENUPOPUP:
        {
            if (wParam)
            {
                MENUINFO MenuInfo = {0};
                MenuInfo.cbSize = sizeof(MenuInfo);
                MenuInfo.fMask = MIM_MENUDATA;
                GetMenuInfo(reinterpret_cast<HMENU>(wParam), &MenuInfo);

                MenuItem* menu = reinterpret_cast<MenuItem*>(MenuInfo.dwMenuData);
                if(menu)
                {
                    std::wcout<<L"CLOSED: "<<menu->GetText()<<L"\n";
                }
            }
        }
        break;

        case WM_MENUSELECT:
        {
            if(((HIWORD(wParam) & MF_HILITE) || (HIWORD(wParam) & MF_MOUSESELECT)) && LOWORD(wParam))
            {
                bool isMainMenu = reinterpret_cast<HMENU>(lParam) == GetMenu(hwnd);
                MENUITEMINFOW ItemInfo = {0};
                ItemInfo.cbSize = sizeof(ItemInfo);
                ItemInfo.fMask = MIIM_DATA;
                GetMenuItemInfoW(reinterpret_cast<HMENU>(lParam), LOWORD(wParam), isMainMenu, &ItemInfo);

                MenuItem* menu = reinterpret_cast<MenuItem*>(ItemInfo.dwItemData);
                if(menu)
                {
                    std::wcout<<L"HOVERED: "<<menu->GetText()<<L"\n";
                }
            }
        }
        break;

        case WM_DESTROY:
        {
            for(auto it : menus)
            {
                delete it;
            }
            menus.clear();
            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;
}

Edited 1 Year Ago by triumphost

Comments
thanks for all my friend

finally i correct that:

//when the mouse move, enter, leave and leave the menu
                case WM_MENUSELECT:
                {
                    static int Last_Menu_ID = -1;
                    if(((HIWORD(wParam) & MF_HILITE) || (HIWORD(wParam) & MF_MOUSESELECT)) && GetMenuState((HMENU)lParam,LOWORD(wParam),MF_BYCOMMAND)!=0xFFFFFFFF)
                    {
                        //mouse leave the previous menu item
                        if(GetMenuState((HMENU)lParam,Last_Menu_ID,MF_BYCOMMAND)!=0xFFFFFFFF)
                        {
                            MENUITEMINFO menuInfo;
                            menuInfo.cbSize = sizeof(MENUITEMINFO);
                            menuInfo.fMask=MIIM_DATA;
                            GetMenuItemInfo((HMENU)lParam,Last_Menu_ID, FALSE, &menuInfo );
                            Menu *mMenu = (Menu *) menuInfo.dwItemData;
                            if (mMenu!=NULL)
                            {
                                mMenu->Leave();
                            }
                        }
                        //Mouse Enter on actual menu item
                        MENUITEMINFO menuInfo1;
                        menuInfo1.cbSize = sizeof(MENUITEMINFO);
                        menuInfo1.fMask=MIIM_DATA;
                        GetMenuItemInfo((HMENU)lParam,LOWORD(wParam), FALSE, &menuInfo1 );
                        Menu *mMenu = (Menu *) menuInfo1.dwItemData;
                        if (mMenu!=NULL)
                        {
                            mMenu->Enter();
                        }
                        Last_Menu_ID = LOWORD(wParam);
                    }
                    //testing if the menu item is valid and the Last_Menu_ID isn't -1
                    else if(GetMenuState((HMENU)lParam,LOWORD(wParam),MF_BYCOMMAND)==0xFFFFFFFF && Last_Menu_ID != -1 )
                    {
                        //mouse live the menu
                        MENUITEMINFO menuInfo;
                        menuInfo.cbSize = sizeof(MENUITEMINFO);
                        menuInfo.fMask=MIIM_DATA;
                        GetMenuItemInfo(menuhandle,Last_Menu_ID, FALSE, &menuInfo );
                        Menu *mMenu = (Menu *) menuInfo.dwItemData;
                        if (mMenu!=NULL)
                        {
                            mMenu->Leave();
                        }
                        Last_Menu_ID = -1;
                    }
                    return DefWindowProc(HandleWindow, msg, wParam, lParam);
                }
                break;

thanks for all

This question has already been answered. Start a new discussion instead.