INF: Some CTRL Accelerator Keys Conflict with Edit Controls

ID Number: Q67293

2.00 2.03 2.10 3.00

WINDOWS

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.