Professional UI Solutions
Site Map   /  Register
 
 

Forum

Please Log In to post a new message or reply to an existing one. If you are not registered, please register.

NOTE: Some forums may be read-only if you are not currently subscribed to our technical support services.

Forums » Prof-UIS Tech Support » discarding Keyboard hook message activates main frame menu Collapse All
Subject Author Date
Ed Nafziger May 3, 2010 - 8:39 PM

I want to install a keyboard hook to override accelerators and menu hotkeys.

If I install a keyboard hook using SetWindows HookEx, or use CExtHookSpy, and return true instead of calling CallNextHookEx or CExtHookSpy::OnHookSpyPreTranslateMessage( pMsg ), some keyboard combinations activate the main frame menu, even if I return true to indicate I want to discard the message.

For example, if I add the following to MDIDOCVIEW sample:

added to CMainFrame::CMainFrame()
  HookSpyRegister( __EHSEF_KEYBOARD | __EHSEF_PRE_TRANSLATION );

added to CMainFrame::~CMainFrame()
  HookSpyUnregister( __EHSEF_KEYBOARD | __EHSEF_PRE_TRANSLATION );

added
bool CMainFrame::OnHookSpyPreTranslateMessage( MSG* pMsg )
{
  if( (GetKeyState(VK_MENU) & 0x80) && pMsg->wParam == ’E’ ) // ALT+E
    return true;

  return CExtHookSpy::OnHookSpyPreTranslateMessage( pMsg );
}


and if I press ALT+E, the "File" item in the main frame menu gains focus and becomes activated. Shouldn’t returning true discard the entire series of keyboard up/down messages? Same thing happens for OnHookPostTranslateMessage. What am I doing wrong? Thanks...



Technical Support May 4, 2010 - 1:53 PM

Here is the condition for the ALT key state checking:

 bool bAltKeyIsPressed = ( ( HIWORD(pMsg->lParam) & KF_ALTDOWN ) == 0 ) ? false : true;

Ed Nafziger May 4, 2010 - 3:20 PM

The issue occurs regardless of the method used to detect the ALT key press. If I replace (GetKeyState(VK_MENU) & 0x80) with ( HIWORD(pMsg->lParam) & KF_ALTDOWN ) the same issue occurs. Please see my reply mentioning the use of CMyMenuBar::g_bCancelNearestAltUp to bypass this issue. Thanks.

Technical Support May 5, 2010 - 12:33 PM

The menu bar is not based on thread hooks. All the keyboard processing is implemented in the CExtMenuControlBar::TranslateMainFrameMessage() method which is typically invoked from the CMainFrame::PreTranslateMessage() virtual method. So, any thread wide keyboard hooks must receive the keyboard notifications earlier than menu bar will receive them. That’s why the CMyMenuBar::g_bCancelNearestAltUp flag can be set to true in your CMainFrame::OnHookSpyKeyMsg() method. But you can do the same in the CMainFrame::PreTranslateMessage() virtual method before invoking the CExtMenuControlBar::TranslateMainFrameMessage() method. I.e. you don’t need thread wide hooks.

But you also can do the same using hooks. Please take a look at the source code of the CMainFrame::OnHookSpyKeyMsg() message posted in this forum thread. We think the following line of code:

if( WM_KEYFIRST <= pMsg->message <= WM_KEYLAST )

Should be replaced with the following one:
if( WM_KEYFIRST <= pMsg->message && pMsg->message <= WM_KEYLAST )

Ed Nafziger May 4, 2010 - 1:27 PM

I have a workaround that seems to fix this. I had to add CMyMenuBar::g_bCancelNearestAltUp = true before returning true in CMainFrame::OnHookSpyKeyMsg and before returning true from translating the accelerators in CMFCApp::PreTranslateMessage.

bool CMainFrame::OnHookSpyKeyMsg( MSG* pMsg )
{
  if( bMacroEnable )
  {
    if( WM_KEYFIRST <= pMsg->message <= WM_KEYLAST )
    {
      if( KeyboardHookProc(HC_ACTION, pMsg->wParam, pMsg->lParam ) ) // my function to do stuff
      {
        CMyMenuBar::g_bCancelNearestAltUp = true; // <<-----
        return true;
      }
    }
  }

  return CExtHookSpy::OnHookSpyKeyMsg( pMsg );
}


BOOL CMFCApp::PreTranslateMessage( MSG* pMsg )
{
  if( TranslateMDISysAccel(hwndMDIClient, pMsg) )
  {
    CMyMenuBar::g_bCancelNearestAltUp = true; // <<-----
    return TRUE;
  }
  if( TranslateAccelerator(hwndFrame, (HACCEL)hAccel, pMsg) )
  {
    CMyMenuBar::g_bCancelNearestAltUp = true; // <<-----
    return TRUE;
  }
  if( ::IsDialogMessage(hWrkDlg, pMsg) )
    return TRUE;

  return __super::PreTranslateMessage( pMsg );
}


is there a better way to fix this?