INFO: Some CTRL Accelerator Keys Conflict with Edit Controls

ID: Q67293


The information in this article applies to:


SUMMARY

Some keys produce the same ASCII values as CTRL+key combinations. These keys conflict with edit controls if one of the CTRL+key combinations is used as a keyboard accelerator.

The following table lists some of the conflicting keys.


   ASCII Value  Key Combination  Equivalent  Windows Virtual Key
   -----------  ---------------  ----------  -------------------
   0x08         CTRL+H           BACKSPACE   VK_BACK
   0x09         CTRL+I           TAB         VK_TAB
   0x0D         CTRL+M           RETURN      VK_RETURN 

For example, consider the following scenario:
  1. CTRL+H has been assigned as an accelerator keystroke to invoke Help


  2. An edit control has the focus


  3. BACKSPACE is pressed to erase the previous character in the edit control


This results in Help being invoked because pressing BACKSPACE is equivalent to pressing CTRL+H. The edit control does not receive the BACKSPACE key press that it requires because TranslateAccelerator() encounters the 0x08 ASCII value and invokes the action assigned to that accelerator. This limitation is caused by the use of the ASCII key code for accelerators instead of the system-dependent virtual key code.


MORE INFORMATION

When messages for the edit control are processed in a message loop that translates accelerators, this translation conflict will occur. Child windows and modeless dialog boxes are the most common situations where this happens.

The affected keystrokes are translated during the processing of the WM_KEYDOWN message for the letter. For example, when the user types CTRL+H, a WM_KEYDOWN is processed for the CTRL key, then another WM_KEYDOWN is processed for the letter "H". In response to this message, TranslateAccelerator() posts a WM_COMMAND message to the owner of the CTRL+H accelerator. Similarly, when the user presses the BACKSPACE key, a WM_KEYDOWN is generated with VK_BACK as the key code. Because the ASCII value of BACKSPACE is the same as that for CTRL+H, TranslateAccelerator() treats them as the same character. Either sequence will cause a WM_COMMAND message to be sent to the owner of the CTRL+H accelerator, which deprives the child window with the input focus of the BACKSPACE key message.

Because this conflict is inherent to ASCII, the safest way to avoid the difficulty is to avoid using the conflicting sequences as accelerators. Any other ways around the problem may be version dependent rather than a permanent fix.

A second way around the situation is to subclass each edit control that is affected. In the subclass procedure, watch for the desired key sequence(s). The following code sample demonstrates this procedure:


   /* This code subclasses a child window edit control to allow it to
    * process the RETURN and BACKSPACE keys without interfering with the
    * parent window's reception of WM_COMMAND messages for its CTRL+H
    * and CTRL+M accelerator keys.
    */ 

   /* forward declaration */ 
   long FAR PASCAL NewEditProc(HWND, unsigned, WORD, LONG);

   /* required global variables */ 
   FARPROC lpfnOldEditProc;
   HWND    hWndOwner;

   /* edit control creation in MainWndProc */ 

   TEXTMETRIC tm;
   HDC hDC;
   HWND hWndEdit;
   FARPROC lpProcEdit;

   ...

    case WM_CREATE:

       hDC = GetDC(hWnd);
       GetTextMetrics(hDC, &tm);
       ReleaseDC(hWnd, hDC);

       hWndEdit = CreateWindow("Edit", NULL,
           WS_CHILD | WS_VISIBLE | ES_LEFT | WS_BORDER,
           50, 50, 50 * tm.tmAveCharWidth, 1.5 * tm.tmHeight,
           hWnd, 1, hInst, NULL);

       lpfnOldEditProc = (FARPROC) GetWindowLong (hWndEdit, GWL_WNDPROC);
       lpProcEdit = MakeProcInstance ((FARPROC) NewEditProc, hInst);
       SetWindowLong(hWndEdit, GWL_WNDPROC, (LONG) lpProcEdit);
       break;

   ...
   /* subclass procedure */ 

   long FAR PASCAL NewEditProc(HWND hWndEditCtrl, unsigned iMessage,
                               WORD wParam, LONG lParam )
   {
     MSG  msg;

     switch (iMessage)
       {
     case WM_KEYDOWN:
       switch (wParam)
           {
       case VK_BACK:
           // This assumes that the next message in the queue will be a
           // WM_COMMAND for the window which owns the accelerators. If
           // this edit control were in a modeless dialog box, hWndOwner
           // should be set to NULL. It may also be NULL in this case.
           PeekMessage(&msg, hWndOwner, 0, 0, PM_REMOVE);

           // Since TranslateAccelerator() processed this message as an
           // accelerator, a WM_CHAR message must be supplied manually to
           // the edit control.
           SendMessage(hWndEditCtrl, WM_CHAR, wParam, MAKELONG(1, 14));
           return 0L;

       case VK_RETURN:
           // Same procedures here.
           PeekMessage(&msg, hWndOwner, 0, 0, PM_REMOVE);
           SendMessage(hWndEditCtrl, WM_CHAR, wParam, MAKELONG(1, 28));
           return 0L;
           }
           break;
       }
       return CallWindowProc(lpfnOldEditProc, hWndEditCtrl, iMessage,
                             wParam, lParam);
   } 

NOTE: Be sure to export the subclass function in the DEF file.


Keywords          : kbcode kbCtrl kbEditCtrl kbNTOS kbGrpUser kbWinOS kbWndw kbWndwMsg 
Version           : 
Platform          : 
Issue type        : kbinfo 

Last Reviewed: March 6, 1999