Tech Support > Microsoft Windows > Development Resources > WM_KEYDOWN on a subclassed edit control eating every ctrl combination
WM_KEYDOWN on a subclassed edit control eating every ctrl combination
Posted by Shin on August 14th, 2006


I want to catch a few of the ctrl combinations of the edit control, but not
all of them. I want to catch CTRL-X and CTRL-V, but want the Ctrl-left and
Ctrl-right combinations to still work. What's a good approach?

Win32API + C++
The edit box is subclassed, and I'm already catching Enter in the subclass.


doing

case WM_CHAR: // will keep the alphabetical keys from working like usual
case WM_KEYDOWN: // will keep ctrl from working like usual

Posted by John Carson on August 14th, 2006


"Shin" <me@anywherebuthere.com> wrote in message
news:Xns981EE1160D88Fmeanywherebutherecom@140.99.9 9.130
Probably the cleanest way to do it is with an accelerator key.

The problem with using a subclass is that you must contend with undocumented
behaviour. For example, if you hit CTRL-V, then a WM_KEYDOWN message is
received with a wParam value of 'V'. So far so good. Unfortunately, you also
get a WM_CHAR message with a wParam value of 22 (at least on XP). This is
undocumented. Further, the default processing for CTRL-V occurs in response
to the WM_CHAR message, so you can only turn off default processing by
catching WM_CHAR messages with a wParam value of 22. But since this
behaviour is undocumented, you can't be sure it will work on other versions
of the OS.

An alternative would be to add code to your application's message loop to
intercept the relevant WM_KEYDOWN messages. However, I think that an
accelerator key is cleaner.

--
John Carson



Posted by Norman Bullen on August 14th, 2006


John Carson wrote:

The OP did not say whether this is happening in a dialog, but if it is
in a modal dialog, accelerators and handling in the message loop are not
options.

Ctrl-X and Ctrl-V are the standard system-wide shortcuts for "cut" and
"paste", respectively. I strongly advise against changing their behavior
on a single window of a single application; it will cause confusion for
the user.

If, however, you must change the behavior of Ctrl-X and Ctrl-V, you can
make use of the fact that they are system-wide shortcuts. When Ctrl-X is
pressed your Edit control will receive a WM_CUT message. When Ctrl-V is
pressed your Edit control will receive a WM_PASTE message. If you catch
those messages in your subclass routine, handle them as you like, and
then return zero, the standard cut and paste actions do not happen.

Note that this translation may be implemented in the dialog handlers in
DialogBox() and IsDialogMessage(). I didn't test in a non-dialog window.
If you find that this doesn't happen in a non-dialog environment you can
use accelerators as John suggested. Be aware, however, that
TranslateAcelerator() will translate the character combinations
regardless of the window to which they are sent and then send WM_COMMAND
messages to the window passed in the call to TranslateAccelerator().
This may not be what you want.

Another possibility (if the system translation to WM_CUT and WM_PASTE
doesn't work) is catch WM_KEYDOWN in your subclass routine and use
GetKeyState() to determine whether both Ctrl and X or V are down at the
same time.

Norm

--
--
To reply, change domain to an adult feline.


Posted by John Carson on August 14th, 2006


"Norman Bullen" <norm@BlackKittenAssociates.com.INVALID> wrote in
message news:gk%Dg.4942$Qf.1273@newsread2.news.pas.earthli nk.net
Quite correct. I should have considered that (I just happen to have some
code for a non-dialog window, so I was thinking of that). You can of course
always simulate a modal dialog with a modeless one that disables its owner
when launched and enables it when closed, but that is a hassle that the OP
might prefer to avoid.

I did test in a non-dialog window and your WM_CUT and WM_PASTE technique
works just fine.

Good point. It is what I wanted in my app, but this may not be true for
others.

Yes, but that has the problem with WM_CHAR messages that I mentioned in my
first post.

--
John Carson



Posted by Shin on August 15th, 2006


"John Carson" <jcarson_n_o_sp_am_@netspace.net.au> wrote in
news:44e092e1$0$17548$61c65585@un-2park-reader-01.sydney.pipenetworks.com
..au:

Yeah, I could tell that this was a tricky issue.


Posted by Shin on August 15th, 2006


"John Carson" <jcarson_n_o_sp_am_@netspace.net.au> wrote in
news:ebp869$2gno$1@otis.netspace.net.au:

Hm... I had no idea that the accellerator key could do that. I'll give it
a shot. Thanks.


Posted by Shin on August 15th, 2006


Shin <me@anywherebuthere.com> wrote in
news:Xns981FD8DACD45meanywherebutherecom@140.99.99 .130:

No luck with the accelerator key. I'll have to play with the keys later
to see what exactly they can do.


Posted by Shin on August 15th, 2006


Shin <me@anywherebuthere.com> wrote in
news:Xns981FEA9BCC12Fmeanywherebutherecom@70.168.8 3.30:

Update: turns out that accelerator keys in general aren't working for
some weird reason. I changed the Ctrl-X to F1 and still nada. Bleh.


Posted by John Carson on August 16th, 2006


"Shin" <me@anywherebuthere.com> wrote in message
news:Xns982061FC2B0D2meanywherebutherecom@70.168.8 3.30

So show some code indicating what you are doing.

--
John Carson



Posted by Shin on August 16th, 2006


"John Carson" <jcarson_n_o_sp_am_@netspace.net.au> wrote in news:ebv0uo
$1801$1@otis.netspace.net.au:

Ah, why not. I plan to make the thing open-source later. The code DOES
need cleaning, but right now I'm just mucking around with things to
figure out certain thing in win32

//------------------------
// NASSAC.h
//------------------------


#define PROGRAM_NAME "YASSAC pre-alpha"

#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <cstring>
#include <string>
using std::basic_string;
#include <commctrl.h>
#include <vector>
using std::vector;
#include "resource.h"

#define ID_EDIT_CTRL 15001
#define EDIT_ENTER_PRESSED 15002
#define BUTTON_GRAB 16001
#define BUTTON_PLAY 16002
#define ID_LISTBOX1 17001


LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK LineListProc (HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK EditSubProc(HWND hwnd, UINT message, WPARAM wParam,
LPARAM lParam);

WNDPROC EditProc;
HWND winprochwnd;

HMENU mainhMenu;
HWND hwndEdit;

HWND hwndGrabButton;
HWND hwndPlayButton;

class ssString : public basic_string<TCHAR>
{
};

class NASSAC
{
public:
NASSAC();

int LineListTop;
int idFocus;

bool doNyanko;

unsigned int currentPosition; // position of the line currently being
edited
unsigned int topLine; // top of the bottom section: the part
with the listing of the line info
unsigned int bottomLine;

unsigned int wavBoxTop;
unsigned int wavBoxBotton;
unsigned int wavBoxLeft;
unsigned int wavBoxRight;

HWND mainhwnd;

HWND listBoxHwnd;

private:
void GenerateConfig();
};

class WaveWindow
{
public:
LRESULT CALLBACK WaveWindowProc(HWND hwnd, UINT message, WPARAM
wParam, LPARAM lParam);
private:
HWND myParent;
};

WaveWindow wavewindow;
NASSAC nas;



//------------------------
// NASSAC.cpp
//------------------------



#include "NASSAC.h"
#include "resource.h"

NASSAC nassac;

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT (PROGRAM_NAME) ;
HWND hwnd ;
MSG msg ;
WNDCLASS wndclass ;

mainhMenu = LoadMenu(hInstance, MAKEINTRESOURCE(IDR_MENU1) );

wndclass.style = CS_HREDRAW | CS_VREDRAW ;
wndclass.lpfnWndProc = WndProc ;
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra = 0 ;
wndclass.hInstance = hInstance ;
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName ;

HACCEL hAccel;

hAccel = LoadAccelerators (hInstance, szAppName );

if (!RegisterClass (&wndclass))
{
MessageBox (NULL, TEXT ("Program requires Windows NT!"),
szAppName, MB_ICONERROR) ;
return 0 ;
}

hwnd = CreateWindow (szAppName, TEXT (PROGRAM_NAME),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
800, 570,
NULL, mainhMenu, hInstance, NULL) ;

nas.mainhwnd = hwnd;

ShowWindow (hwnd, iCmdShow) ;
UpdateWindow (hwnd) ;

while (GetMessage (&msg, NULL, 0, 0))
{
if ( !TranslateAccelerator (hwnd, (HACCEL) hAccel, &msg) )
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
}
return msg.wParam ;
}

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM
lParam)
{
HDC hdc ;
PAINTSTRUCT ps ;
TCHAR buffer[200];

RECT rect ;
RECT wavRect;

int cxClient, cyClient;

int ipos;

TCHAR tcbuffer [32769]; // max size of a windows text buffer

HBRUSH hBrush;

winprochwnd = hwnd;

//HWND hwndEdit;//the edit box we want to process messages for
long CALLBACK SubProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM
lParam);

switch(message)
{
case WM_CREATE:

_stprintf(buffer, TEXT("x\0"), tcbuffer );

GetClientRect (hwnd, &rect) ;

cxClient = rect.right;
cyClient = rect.bottom;

// Edit box position

if ( nassac.LineListTop == 0 )
{
nassac.LineListTop = rect.bottom / 50;
}

// Edit box in the middle

hwndEdit = CreateWindow( TEXT ("edit"), NULL, WS_CHILD | WS_VISIBLE
|
WS_BORDER | ES_LEFT | ES_AUTOVSCROLL, 0, 200, rect.right, 24,
// single-line edit box
//WS_BORDER | ES_LEFT | ES_MULTILINE, 0, 200, rect.right, 56,
hwnd, (HMENU) ID_EDIT_CTRL, ((LPCREATESTRUCT) lParam)->
hInstance, NULL);

// Edit box overrides for things like responding to the Enter key
and any additional right mouse button clicks

EditProc = (WNDPROC)GetWindowLong(hwndEdit, GWL_WNDPROC);
SetWindowLong(hwndEdit, GWL_WNDPROC, (long)EditSubProc);

// Buttons

hwndGrabButton = CreateWindow( TEXT ("button"), TEXT("Grab
Selected"), WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
140, 140, 160, 53,
hwnd, (HMENU) BUTTON_GRAB,
((LPCREATESTRUCT) lParam)->hInstance, NULL);

hwndPlayButton = CreateWindow( TEXT ("button"), TEXT("Play
Selected"), WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
320, 140, 160, 53,
hwnd, (HMENU) BUTTON_PLAY,
((LPCREATESTRUCT) lParam)->hInstance, NULL);

// List boxes

// http://msdn.microsoft.com/library/de...l=/library/en-
us/shellcc/platform/commctls/listboxes/listboxreference/listboxmessages/l
b_settabstops.asp
// http://support.microsoft.com/default...b;en-us;816176

nas.listBoxHwnd = CreateWindow( TEXT ("listbox"), TEXT("#"),
WS_CHILD | WS_VISIBLE | LBS_USETABSTOPS | // LBS_MULTICOLUMN |
LBS_MULTIPLESEL |
LBS_NOTIFY, 0, 0, rect.right, 140,
hwnd,
(HMENU) ID_LISTBOX1, (HINSTANCE)
GetWindowLong(hwnd, GWL_HINSTANCE), NULL);

return 0;

case WM_SIZE:
cxClient = LOWORD(lParam);
cyClient = HIWORD(lParam);
return 0;

case WM_PAINT:

hdc = BeginPaint (hwnd, &ps) ;


GetClientRect (hwnd, &rect) ;

// wav file window
SetRect(&wavRect, rect.left, nas.wavBoxTop, rect.right,
nas.wavBoxBotton);

hBrush = CreateSolidBrush( RGB(10, 255, 10) );

hdc = GetDC(hwnd);

FillRect(hdc, &wavRect, hBrush);

MoveToEx(hdc, wavRect.left, wavRect.top, NULL);
LineTo (hdc, wavRect.right, (wavRect.top + wavRect.bottom) / 2);

EndPaint (hwnd, &ps);

DeleteObject(hBrush);
ReleaseDC(hwnd, hdc);

return 0 ;

case WM_COMMAND:

switch( LOWORD(wParam ) )
{

case ID_ACCELERATOR_VKF1:

_stprintf(tcbuffer, TEXT("CTRL-X") );
SendMessage(hwnd, WM_SETTEXT, sizeof(tcbuffer), (LPARAM) (TCHAR
* )tcbuffer );

return 0;

case ID_FILE_EXIT:

SendMessage(hwnd, WM_CLOSE, 0, 0);
return 0;

case BUTTON_GRAB:
_stprintf(tcbuffer, TEXT("Don't grab me like that!") );
SendMessage(hwnd, WM_SETTEXT, sizeof(tcbuffer), (LPARAM) (TCHAR
* )tcbuffer );

return 0;

case BUTTON_PLAY:

_stprintf(tcbuffer, TEXT("Play sound or something...") );
SendMessage(hwnd, WM_SETTEXT, sizeof(tcbuffer), (LPARAM) (TCHAR
* )tcbuffer );

return 0;
}

break;

case EDIT_ENTER_PRESSED:

_stprintf(tcbuffer, TEXT("Enter Pressed!") );
SendMessage(hwndEdit, WM_SETTEXT, sizeof(tcbuffer), (LPARAM) (TCHAR
* )tcbuffer );

return 0;


case WM_DESTROY:
PostQuitMessage (0) ;
return 0;

}

return DefWindowProc(hwnd, message, wParam, lParam);
}

LRESULT CALLBACK WaveWindow::WaveWindowProc (HWND hwnd, UINT message,
WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
RECT rect;

switch(message)
{

case WM_CREATE:
myParent = GetParent(hwnd);
return 0;

case WM_KEYDOWN: // I want the controls to be passed to the main
window
SendMessage( myParent, message, wParam, lParam);
return 0;
}


return DefWindowProc(hwnd, message, wParam, lParam);
}

//// Class stuff here

NASSAC::NASSAC()
{
LineListTop = 0;

currentPosition = 0;
topLine = 0;
bottomLine = 0;

wavBoxTop = 300;
wavBoxBotton = 566;
wavBoxLeft = 0;
wavBoxRight = 800;

doNyanko = false;
}

LRESULT CALLBACK EditSubProc(HWND hwnd, UINT message, WPARAM wParam,
LPARAM lParam)
{
int iState;
bool passBack = false;

UINT msgback = message;

/*
if ( GetKeyState ( VK_CONTROL) < 0 ) // we lose CTRL-LEFT and CTRL-
RIGHT but that's a minor loss
{
SendMessage( (nas.mainhwnd), message, wParam, lParam);
}
*/

switch(message)
{

case WM_KEYUP:

switch(LOWORD(wParam))
{
case VK_RETURN:
nas.doNyanko = true;
SendMessage( (nas.mainhwnd), EDIT_ENTER_PRESSED, 0, 0);
return 0;
default:
break;
}

default:
return CallWindowProc(EditProc, hwnd, message, wParam, lParam);
}
return TRUE;
}





//------------------------
// resource.h
//------------------------


//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by YASSAC.rc
//
#define IDR_MENU1 101
#define IDR_ACCELERATOR1 102
#define ID_FILE_EXIT 40001
#define ID_FILE_NEW40002 40002
#define ID_ACCELERATOR_VKF1 40003

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 103
#define _APS_NEXT_COMMAND_VALUE 40005
#define _APS_NEXT_CONTROL_VALUE 1001
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif


/////////////////
// YASSAC.rc //
/////////////////

// Microsoft Visual C++ generated resource script.
//
#include "resource.h"

#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////
////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "afxres.h"

/////////////////////////////////////////////////////////////////////////
////
#undef APSTUDIO_READONLY_SYMBOLS

/////////////////////////////////////////////////////////////////////////
////
// English (U.S.) resources

#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
#ifdef _WIN32
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#pragma code_page(1252)
#endif //_WIN32

#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////
////
//
// TEXTINCLUDE
//

1 TEXTINCLUDE
BEGIN
"resource.h\0"
END

2 TEXTINCLUDE
BEGIN
"#include ""afxres.h""\r\n"
"\0"
END

3 TEXTINCLUDE
BEGIN
"\r\n"
"\0"
END

#endif // APSTUDIO_INVOKED


/////////////////////////////////////////////////////////////////////////
////
//
// Menu
//

IDR_MENU1 MENU
BEGIN
POPUP "&File"
BEGIN
MENUITEM "New", ID_FILE_NEW40002
MENUITEM SEPARATOR
MENUITEM "Exit", ID_FILE_EXIT
END
END


/////////////////////////////////////////////////////////////////////////
////
//
// Accelerator
//

IDR_ACCELERATOR1 ACCELERATORS
BEGIN
VK_F1, ID_ACCELERATOR_VKF1, VIRTKEY, NOINVERT
END

#endif // English (U.S.) resources
/////////////////////////////////////////////////////////////////////////
////



#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////
////
//
// Generated from the TEXTINCLUDE 3 resource.
//


/////////////////////////////////////////////////////////////////////////
////
#endif // not APSTUDIO_INVOKED



Posted by John Carson on August 16th, 2006


"Shin" <me@anywherebuthere.com> wrote in message
news:Xns98216D54693A5meanywherebutherecom@140.99.9 9.130
I wasn't anticipating you would show the whole thing but...

[snip]

Here is your problem. Try:

hAccel = LoadAccelerators (hInstance, MAKEINTRESOURCE(IDR_ACCELERATOR1));

--
John Carson



Posted by Shin on August 17th, 2006


"John Carson" <jcarson_n_o_sp_am_@netspace.net.au> wrote in news:ebvfpu
$1c87$1@otis.netspace.net.au:

Most of the tutorials I read had what looked like an arbitrary char[].
That makes MUCH more sense to me, plus, it works! Thanks for the help.



Similar Posts