c00000fd
c00000fd

Reputation: 22255

Calling TrackPopupMenu() after a spacebar key press on CLinkCtrl causes a warning message beep. Why?

Bear with me. I'll have to give you my setup.

I have a dialog window with a syslink-control on it (CLinkCtrl):

enter image description here

that can be clicked to display a context menu.

I'm now trying to set up an accessibility option for a user to press a spacebar or hit enter on the keyboard to display that context menu:

enter image description here

I'm using the NM_RETURN notification for that:

LRESULT CTestMfcLinkCtrlDlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
    // TODO: Add your specialized code here and/or call the base class

    if(message == WM_NOTIFY)
    {
        NMHDR* pNmhdr = (NMHDR*)lParam;
        if(pNmhdr->code == NM_RETURN)
        {
            showMenu();
        }
    }

    return CDialogEx::WindowProc(message, wParam, lParam);
}

And the menu itself is shown as follows:

void CTestMfcLinkCtrlDlg::showMenu()
{
    HMENU hMMenu = LoadMenu(GetModuleHandle(NULL),
        MAKEINTRESOURCE(IDR_MENU1));
    HMENU hMenu = GetSubMenu(hMMenu, 0);

    if(hMenu)
    {
        HWND hParentWnd = this->GetSafeHwnd();

        CWnd* pW = this->GetDlgItem(IDC_SYSLINK1);
        CRect rcW;
        pW->GetWindowRect(&rcW);

        UINT iCmdRes = ::TrackPopupMenu(hMenu, 
            TPM_TOPALIGN | TPM_LEFTALIGN | TPM_LEFTBUTTON | 
            TPM_VERPOSANIMATION | TPM_HORPOSANIMATION | TPM_RETURNCMD, 
            rcW.left, rcW.bottom, 
            0, hParentWnd, NULL);

        switch(iCmdRes)
        {
            //...
        }

    }

    DestroyMenu(hMMenu);
}

IDR_MENU1 is taken from the resources:

enter image description here

So what happens is this: if the system-link control has keyboard focus, press spacebar or the enter (return) key on the keyboard. My context menu will be displayed, but then at the same time you will hear a message beep, what looks like with the MB_ICONWARNING parameter. I did some debugging and this message beep comes from the TrackPopupMenu call.

Any idea why is it doing it and how to prevent that warning beep?

Here's the link to VS2017 MFC solution that I was testing it on.

Upvotes: 2

Views: 249

Answers (1)

Barmak Shemirani
Barmak Shemirani

Reputation: 31599

The popup menu is opened in response to WM_CHAR message. It seems the menu is opened before the WM_CHAR is fully processed. So the popup menu receives the same WM_CHAR message. The menu doesn't know what to do with that key and complains with annoying beep.

If you press the space bar while the menu is open, you will hear the same beep.

Solution, run the default function first:

LRESULT CMyDialog::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
    LRESULT lres = CDialogEx::WindowProc(message, wParam, lParam);

    if(message == WM_NOTIFY)
    {
        NMHDR* pNmhdr = (NMHDR*)lParam;
        if(pNmhdr->code == NM_RETURN)
        {
            showMenu();
        }
    }

    return lres;
}

Or, do this in response to ON_NOTIFY, but make sure to remove the message from the thread by using

PeekMessage(&msg, NULL, WM_CHAR, WM_CHAR, PM_REMOVE);

Example:

void CMyDialog::showMenu()
{
    CMenu menu;
    menu.LoadMenu(IDR_MENU1);
    ASSERT(menu.GetSafeHmenu());
    CMenu *popup = menu.GetSubMenu(0);
    ASSERT(popup);

    CRect rc;
    CWnd *syslink = GetDlgItem(IDC_SYSLINK1);
    ASSERT(syslink);
    syslink->GetWindowRect(&rc);

    MSG msg;
    if (::PeekMessage(&msg, NULL, WM_CHAR, WM_CHAR, PM_NOREMOVE))
        AfxGetThread()->PumpMessage();

    UINT iCmdRes = popup->TrackPopupMenu(
            TPM_TOPALIGN | TPM_LEFTALIGN | TPM_LEFTBUTTON |
            TPM_VERPOSANIMATION | TPM_HORPOSANIMATION | TPM_RETURNCMD,
            rc.left, rc.bottom, this);

    switch(iCmdRes)
    {
        //...
    }
}

Upvotes: 2

Related Questions