一、问题描述
在写聊天程序的时候,想要通过回车结束输入,进行消息发送,结果发现编辑框(edit)在编辑时,无法响应WM_KEYDOWN的消息。
二、解决方案
编辑框(edit)子窗口控件无法响应同等消息循环下的WM_KEYDWON以及按键消息,需要给子窗口控件设置单独的回调函数。
2.1 函数介绍
这里我们使用SetWindowLong函数帮助我们完成设置。先来看函数原型。
Long SetWindowLong(HWND hWnd,int nIndex,LONG dwNewLong)
#ifdef UNICODE
#define SetWindowLong SetWindowLongW
#else
#define SetWindowLong SetWindowLongA
#endif // !UNICODE
MSDN上的解释:https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwindowlonga
百度百科上的解释:https://baike.baidu.com/item/SetWindowLong
第一个参数HWND是: 窗口句柄及间接给出的窗口所属的类。
第二个参数int是:指定将设定的大于等于0的偏移值。有效值的范围从0到额外类的存储空间的字节数减4:例如若指定了12或多于12个字节的额外窗口存储空间,则应设索引位8来访问第三个4字节,同样设置0访问第一个4字节,4访问第二个4字节。要设置其他任何值,可以指定下面值之一:
(来自百度百科)
第三个参数LONG是:指定的替换值。
如果函数成功,返回值是指定的32位整数的原来的值。如果函数失败,返回值为0。若想获得更多错误信息,请调用GetLastError函数。
如果指定32位整数的原来的值为0,并且函数成功,则返回值为0,但是函数并不清除最后的错误信息,这就很难判断函数是否成功。这时,就应在调用SetWindowLong之前调用SetLastError(0)函数来清除最后的错误信息。这样,如果函数失败就会返回0,并且GetLastError。也返回一个非零值。
三、代码实现(只罗列了关键部分!)
#include<Windows.h>
//只罗列代码框架
//原始的回调函数,即edit子窗口所在父窗口的回调函数
LRESULT CALLBACK WinProc(HWND, UINT, WPARAM, LPARAM);
//想要为edit编辑框设置的回调函数
LRESULT CALLBACK EditProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR szCmdLine,
int iCmdShow) {
......
wndClass.lpfnWndProc = WinProc;//主回调函数
......
......
}
LRESULT CALLBACK WinProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam){
static HWND hwndEdit;//编辑框句柄
static LONG OldProc;//保存老的WNDPROC
switch(msg){
case WM_CREATE:{//在WM_CREATE中创建窗口
//调用CreateWindow函数创建子窗口控件,接收子窗口句柄
hwndEdit = CreateWindow(L"edit", L"",
WS_CHILD | WS_VISIBLE | ES_LEFT | ES_MULTILINE,
0, 0, 0, 0, hWnd, (HMENU)1, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
return 0;
}
case WM_SIZE:{//在WM_SIZE中构筑窗口
//调用MoveWindow函数重绘窗口
MoveWindow(hwndEdit, x坐标, y坐标, 长, 宽, TRUE);
//在窗口绘制完成后,使用SetWindowLong函数设置自己的回调函数
//并将老的回调函数保存下来
OldProc = SetWindowLong(hwndEdit, GWL_WNDPROC, (LONG)EditProc);
return 0;
}
......
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}
LRESULT CALLBACK EditProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_KEYDOWN: {
if (wParam == VK_RETURN) {//以回车消息为例
MessageBox(NULL, L"回车键按下", L"提示", MB_OK);
}
return 0;
}
}
//这里一定要注意,我们只处理我们想要处理的消息,我们不需要处理的消息放给原来的回调函数处理
return CallWindowProc((WNDPROC)OldProc, hWnd, msg, wParam, lParam);
}