Sign in to follow this  
Khatharr

[Win32 / C++] Edit control does not generate WM_COMMAND when return key is pressed

Recommended Posts

Okay, so here's yet another bizzare hurdle I've come up against. I have my app set up so that there's an edit box and a button. When the button is pressed the text in the edit box is fetched, added to a class that handles lines of text and then cleared. What I want is for the box to accept the return key in order to produce the same effect. I read this article on MSDN:

http://support.microsoft.com/kb/102589

And it says the box should send a WM_COMMAND with the wParam IDOK, but after piping all WM_COMMAND messages to a display it turns out that it does no such thing. I've tried setting up the proc to intercept WM_KEYDOWN but when the edit control has the focus the proc appears to not get those messages. Anyway, here's the code I have so far. The large edit box is being used temporarily as a debugging tool as well as the display, lol. When a WM_COMMAND is received by the proc it will print out the hiword and loword of the wParam. Try not to click the listbox on the left or the large text box as they send a flurry of WM_COMMANDS that will cause the thing to crash. There's 3 files here:

main.cpp:
----------------------------------------------------
#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>
#include <richedit.h>
#include <cstdio>
#include "class_MessageLines.h"

#define IDC_LIST 101
#define IDC_TEXT 102
#define IDC_EDIT 103
#define IDC_BUTTON 104

const char g_szClassName[] = "myWindowClass";
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
HFONT hfont0, hfont1;
HWND hcList, hcText, hcEdit, hcButton;
MessageLines lineCl;

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
WNDCLASSEX wc;
ZeroMemory(&wc, sizeof(wc));
wc.cbSize = sizeof(wc);
wc.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1);
wc.lpszMenuName = NULL;
wc.style = CS_HREDRAW|CS_VREDRAW;
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(0, (LPCTSTR)IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.lpszClassName = g_szClassName;
if(!RegisterClassEx(&wc)) {MessageBox(NULL, "Window Registration Failed!", "Error!", MB_ICONEXCLAMATION | MB_OK); return 0;}

hfont0 = CreateFont(-11, 0, 0, 0, 400, FALSE, FALSE, FALSE, 0, 400, 2, 1, 1, ("Microsoft Sans Serif"));
hfont1 = CreateFont(-18, 0, 0, 0, 400, FALSE, FALSE, FALSE, 0, 400, 2, 1, 1, ("Microsoft Sans Serif"));
HWND hwnd = CreateWindowEx(WS_EX_APPWINDOW, g_szClassName, "NetChat (v0.1b)", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 525, 313, 0, 0, hInstance, 0);
hcList = CreateWindowEx(0, WC_LISTBOX, 0,
WS_VISIBLE|WS_CHILD|WS_VSCROLL|WS_BORDER|LBS_NOINTEGRALHEIGHT|LBS_NOSEL|LBS_SORT,
3, 3, 135, 244, hwnd, (HMENU)IDC_LIST, hInstance, 0);
SendMessage(hcList, WM_SETFONT, (WPARAM)hfont0, FALSE);
hcText = CreateWindowEx(0, WC_EDIT, 0,
WS_VISIBLE|WS_CHILD|WS_VSCROLL|WS_BORDER|ES_AUTOHSCROLL|ES_AUTOVSCROLL|ES_MULTILINE|ES_READONLY,
141, 3, 375, 244, hwnd, (HMENU)IDC_TEXT, hInstance, 0);
SendMessage(hcText, WM_SETFONT, (WPARAM)hfont0, FALSE);
hcEdit = CreateWindowEx(0, WC_EDIT, 0, WS_VISIBLE|WS_CHILD|WS_TABSTOP|WS_BORDER|ES_AUTOHSCROLL, 3, 252, 375, 29, hwnd, (HMENU)IDC_EDIT, hInstance, 0);
SendMessage(hcEdit, WM_SETFONT, (WPARAM)hfont1, FALSE);
SendMessage(hcEdit, EM_LIMITTEXT, (WPARAM)150, 0);
hcButton = CreateWindowEx(0, WC_BUTTON, "Send", WS_VISIBLE|WS_CHILD|WS_TABSTOP, 384, 252, 127, 29, hwnd, (HMENU)IDC_BUTTON, hInstance, 0);
SendMessage(hcButton, WM_SETFONT, (WPARAM)hfont0, FALSE);

ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
MSG Msg;
while(GetMessage(&Msg, NULL, 0, 0) > 0) {
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return Msg.wParam;
}

void updateText() {
char buffer[200];
char* cptr;
Edit_GetText(hcEdit, buffer, 200);
Edit_SetText(hcEdit, "");
lineCl.add(buffer);
cptr = lineCl.getlines();
Edit_SetText(hcText, cptr);
SendMessage(hcText, EM_SCROLL, SB_BOTTOM, 0);
delete cptr;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
char temp[100];
char* cptr;
switch(msg) {
case WM_COMMAND:
if((LOWORD(wParam) == IDC_BUTTON) && (HIWORD(wParam) == BN_CLICKED)) {
updateText();
break;
}
sprintf(temp, "LO: %u HI: %u", LOWORD(wParam), HIWORD(wParam));
lineCl.add(temp);
cptr = lineCl.getlines();
Edit_SetText(hcText, cptr);
SendMessage(hcText, EM_SCROLL, SB_BOTTOM, 0);
delete cptr;
break;
case WM_CLOSE: DestroyWindow(hwnd); break;
case WM_DESTROY: PostQuitMessage(0); break;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}


class_MessageLines.h:
----------------------------------------------------
#define MAX_LINES 30

struct MsgLineNode {
struct MsgLineNode* next;
char text[200];
};

class MessageLines {
public:
MessageLines();
~MessageLines();

//removes al lines
void clear();

//adds a line, removing the first line if size is MAX_LINES
void add(const char* line);

//allocates a char string and packs all lines into it with "\r\n" between
//returns the string pointer, which must be 'delete'd after use
char* getlines();
private:
struct MsgLineNode* root;
int numlines;
};


class_MessageLines.cpp:
----------------------------------------------------
#include <cstring>
#include <windows.h>
#include "class_MessageLines.h"

MessageLines::MessageLines() {
root = new struct MsgLineNode;
ZeroMemory(root, sizeof(MsgLineNode));
numlines = 0;
}

MessageLines::~MessageLines() {
clear();
delete root;
}

void MessageLines::clear() {
struct MsgLineNode* node = root;
struct MsgLineNode* temp = node->next;
while(temp != 0) {
node = temp;
temp = node->next;
delete node;
}
numlines = 0;
}

void MessageLines::add(const char* line) {
struct MsgLineNode* node = root;
if(numlines > MAX_LINES) {
node = node->next;
root->next = node->next;
delete node;
--numlines;
}
node = root;
while(node->next != 0) {node = node->next;}
node->next = new struct MsgLineNode;
node = node->next;
node->next = 0;
strcpy(node->text, line);
++numlines;
}

char* MessageLines::getlines() {
char* buffer;
if(numlines == 0) {
buffer = new char;
*buffer = 0;
return buffer;
}

struct MsgLineNode* node = root;
int buf_len = 0;
while(node->next != 0) {
node = node->next;
buf_len += (strlen(node->text) + 3);
}
if(buf_len == 0) {return 0;}
buffer = new char[buf_len];
ZeroMemory(buffer, buf_len);

node = root;
while(node->next != 0) {
node = node->next;
strcat(buffer, node->text);
strcat(buffer, "\r\n");
}
return buffer;
}

Share this post


Link to post
Share on other sites
The article you cite relates to dialog boxes, not to controls created manually in a normal window.

As far as I am aware, the only way to solve this would be to subclass the edit box.

Have a read through the article I've linked and post back if you are still confused.

Share this post


Link to post
Share on other sites
Hmm...

Okay. First of all thank you for the helpful reply.

I think I see how it works, but it's not playing nice. I wrote this proc to test:
LRESULT CALLBACK NewEditBoxProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) {
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}


And then after declaring the edit box I said:
SetWindowSubclass(hcEdit, NewEditBoxProc, 0, 0);


I linked in comctl32.lib and it compiles okay, but when I run it any interaction with the edit box causes an immediate crash. SetWindowSubclass is returning true, indicating success. I'm not sure I understand what 'uIdSubclass' is for so that may be the problem. (I'm guessing that it just needs to be unique per control?)

Thank you for your patience.

Share this post


Link to post
Share on other sites
I haven't actually ever used the new subclassing stuff. Done the old way, you'd do something like:


WNDPROC OldEditProc;
HWND Edit;

LRESULT CALLBACK NewEditProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
// intercept messages

CallWindowProc(OldEditProc,hwnd,msg,wParam,lParam);

// note to never call a wndproc directly - the value returned by the edit
// when you request its current proc is not necessarily a function
// pointer - always use CallWindowProc()
}

void Init()
{
OldEditProc=(WNDPROC)SetWindowLongPtr(Edit,GWLP_WNDPROC,(LONG_PTR)NewEditProc);
}


I'm sure there must be a solution using the new subclassing system as well but I'll leave that to someone more familiar with it.

Share this post


Link to post
Share on other sites
Oh ffs. It was the switch in my window proc. It didn't break after WM_COMMAND and went straight to WM_CLOSE.

Man I wish I could rent a brain some days.

It works just fine now with this proc:
LRESULT CALLBACK NewEditBoxProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) {
if((uMsg == WM_CHAR) && (wParam == VK_RETURN)) {
updateText();
return 0;
}
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}


The DefSubclassProc is actually one of the functions included by the new comctl32. It calls the next proc in the subclassing chain.

Thank you for helping me get this thing to work! :)

[Edited by - Khatharr on September 27, 2010 4:07:38 PM]

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this