What is the right way to setup a global hot key in MFC app?

Viewed 70

I know how to use the accelerator table to setup hot keys within my app. But how do I get a hot key at global level?

I.e. My app is running, but minimised. Yet I still want it to detect and process a hot key.

1 Answers

If you need a system-wide hotkey then you should use RegisterHotKey, passing in the window handle whose window procedure is responsible for handling the event. At the application level this is commonly the CFrameWnd/CFrameWndEx-derived window implementation.

Once the hotkey is registered, the receiving window can observe the event in its custom CWnd::OnHotKey override. Make sure to add the ON_WM_HOTKEY() message handler to the message map of the receiving window implementation.

For a standard, default-generated SDI/MDI application you'd need to apply the following changes.

MainFrm.h:

class CMainFrame : public CFrameWnd
{
    // ...
private:
    // Hot key handler routine
    afx_msg void OnHotKey(UINT nHotKeyId, UINT nKey1, UINT nKey2);
};

MainFrm.cpp:

BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
    // ...

    // Make sure that WM_HOTKEY messages are routed to this window
    ON_WM_HOTKEY()
END_MESSAGE_MAP()

// Hot key ID; can be any value in the range 0x0000 through 0xBFFF
// Allows the application to identify this hot key in case there is more than one
constexpr int MyHotKeyId = 42;

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) {
    // ...

    // Register a global system-wide hot key (Ctrl+Shift+Alt+I)
    if (!::RegisterHotKey(this->m_hWnd, MyHotKeyId,
                          MOD_ALT | MOD_SHIFT | MOD_CONTROL | MOD_NOREPEAT,
                          'I')) {
        auto const ec = ::GetLastError();
        auto const err_msg = std::format(L"RegisterHotKey failed (error: {})\n",
                                         ec);
        ::OutputDebugStringW(err_msg.c_str());

        // Probably a good idea to handle failure more gracefully than this:
        return -1;
    }

    return 0;
}

void CMainFrame::OnHotKey(UINT nHotKeyId, UINT nKey1, UINT nKey2) {
    // Handle hot key; nHotKeyId is MyHotKeyId in this case
}

Note that receiving a WM_HOTKEY message grants the receiving thread foreground activation permission. If you want your application to come to the foreground upon receiving a registered hot key, you can use the following OnHotKey implementation:

void CMainFrame::OnHotKey(UINT nHotKeyId, UINT nKey1, UINT nKey2) {
    if (this->IsIconic()) {
        this->ShowWindow(SW_RESTORE);
    }
    this->SetForegroundWindow();
}

No flashing the taskbar button, everything just works as intended.

Related