简单来说,消息分析器就是一段代码,在我的讲述中,将分7重来循序渐进的介绍它.从最初的第1重到最成熟的第7重,它的样子会有很大的变化.但,实现的功 能都是一样的,所不同的,仅仅是变得更加简练罢了.
程序开始时候,是WinMain函数,然后会生成初始的窗口,同时会调用WndProc函数.这是一个自定义的函数,名字也会有变化,但其功能是一样的, 就是运行消息分析器.WndProc函数如下:
LRESULT CALLBACK WndProc (HWND hwnd, UINT msg,WPARAM wParam, LPARAM lParam)
{
//......
return DefWindowProc(hwnd, msg, wParam, lParam);
}
这其中,hwnd是窗口的句柄,msg是系统发送来的消息的名字.wParam和lParam则是随消息一起发送来的消息参数.
WndProc函数使用了消息分析器,下面把消息分析器的内容解释一下:
第一重,当不同的消息出现时,在其中写入相应的程序语句即可。
LRESULT CALLBACK WndProc (HWND hwnd, UINT msg,WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
第二重,运用三个消息分析器进行处理。
LRESULT CALLBACK WndProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
这里的HANDLE_WM_CREATE,HANDLE_WM_PAINT,HANDLE_WM_DESTROY就是消息分析器。
与消息不同之处就是在前面增加了“HANDLE_”字符,windows的消息分析器就是这样的模样。它的本质就是宏定义。其中的四个参数有三个都是从本 函数的入口参数中直接得到的,即为hwnd, wParam, lParam。只有第四的参数是表明调用的函数。
消息分析器是在winowsx.h文件中定义的。由此,可以看出第四个参数是调用的函数,其定义如下:
#define HANDLE_WM_CREATE(hwnd, wParam, lParam, fn) ((fn)((hwnd), (LPCREATESTRUCT)(lParam)) ? 0L : (LRESULT)-1L)
#define HANDLE_WM_PAINT(hwnd, wParam, lParam, fn) ((fn)(hwnd), 0L)
#define HANDLE_WM_DESTROYCLIPBOARD(hwnd, wParam, lParam, fn) ((fn)(hwnd), 0L)
0L是表示int类型的变量,其数值为0。
int类型时,可在后面加l或者L(小写和大写形式)
ussigned 无符号数时,可在后面加u或者U(小写和大写形式)
float类型时,可在后面加f或者F(小写和大写形式)
例如:
128u 1024UL 1L 8Lu 3.14159F 0.1f
LRESULT是一个系统的数据类型,其定义如下:
typedef LONG_PTR LRESULT;
LONG_PTR也是一个系统的数据类型,其定义如下:
#if defined(_WIN64)
typedef __int64 LONG_PTR;
#else
typedef long LONG_PTR;
#endif
由此可见,LRESULT的实质就是64位的long类型的变量
那么(LRESULT)-1L的实质并不是减法,而是((LRESULT)(-1L)),即强制类型转换
第三重,把消息分析器的宏定义代换回去,就成了下面的样子
LRESULT CALLBACK WndProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
在逗号表达式,C++会计算每个表达式,但完整的逗号表达式的结果是最右边表达式的值。所以,会return 0。
然后,就可以手动的编写各个处理函数了:Cls_OnCreate,Cls_OnPaint,WM_DESTROY。
第四重,
LRESULT CALLBACK WndProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
HANDLE_MSG也是一个宏,它在windowsx.h中定义,如下:
#define HANDLE_MSG(hwnd, message, fn) case (message): return HANDLE_##message((hwnd), (wParam), (lParam), (fn))
这个宏要做的就是根据不同的message(##用来连接前后的字符串),把自己“变成”相应的HANDLE_XXXXMESSAGE形式的宏,再通过相 应的宏来执行消息处理代码。说白了,就是把message的消息做为替换,##就是一个替换的标志。如果没有##,就成了HANDLE_message 了,这样,宏是不会被代换的。如果有,则会代换,如hwnd和fn。
比如实际代码中写入:
HANDLE_MSG(hwnd, WM_CREATE, Cls_OnCreate)
则经过转换就变成:
case (WM_CREATE): return HANDLE_WM_CREATE((hwnd), (wParam), (lParam), (Cls_OnCreate))
这与第二重一模一样。
以上四重,是消息分析器的基本使用,但,这不完整,消息分析器主要应用在对话框消息处理中。这里,窗口子类化是我们经常使用的手段,这也可以通过消息分析 器实现,
第五重,
LRESULT CALLBACK Dlg_Proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
}
return false;
}
由于是窗口子类化,所以,最后,返回的是false,以表明,如果没有约定响应的消息,则返回父亲窗口false,如果有,则返回ture,这是与前四重 不同的地方。
一般情况下,对话框过程函数应该在处理了消息的情况下返回TRUE,如果没有处理,则返回FALSE。
如果对话框过程返回了FALSE,那么对话框管理器为这条消息准备默认的对话操作。
但是,这其中有错误,因为有的消息,需要单独处理。单独处理的消息列表见SetDlgMsgResult宏。
第六重,这点小问题,这就需要用到 SetDlgMsgResult(hwnd, msg, result)宏。
LRESULT CALLBACK Dlg_Proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_INITDIALO:
return (SetDlgMsgResult(hwnd, Msg, HANDLE_WM_INITDIALO((hwnd), (wParam), (lParam), (fn)));
case WM_COMMAND:
return (SetDlgMsgResult(hwnd, Msg, HANDLE_WM_COMMAND((hwnd), (wParam), (lParam), (fn)));
}
return false;
}
这里,就用直接用到了第二重的消息分析器,而抛弃了其他。
这个宏定义如下:
#define SetDlgMsgResult(hwnd, msg, result)
(
(
(msg) == WM_CTLCOLORMSGBOX ||
(msg) == WM_CTLCOLOREDIT ||
(msg) == WM_CTLCOLORLISTBOX ||
(msg) == WM_CTLCOLORBTN ||
(msg) == WM_CTLCOLORDLG ||
(msg) == WM_CTLCOLORSCROLLBAR ||
(msg) == WM_CTLCOLORSTATIC ||
(msg) == WM_COMPAREITEM ||
(msg) == WM_VKEYTOITEM ||
(msg) == WM_CHARTOITEM ||
(msg) == WM_QUERYDRAGICON ||
(msg) == WM_INITDIALOG
) ?
(BOOL)(result) :
(SetWindowLongPtr((hwnd), DWLP_MSGRESULT, (LPARAM)(LRESULT)(result)), TRUE)
)
为了表述清楚,所以用了此格式,这是一个条件表达式,首先对消息类型进行考察。如果对话框过程处理的消息恰巧为返回特定值中的一个,则如实返回result;
不要被前面的BOOL蒙蔽,BOOL在头文件中的定义实际上是一个int型,一旦需要返回非TRUE或FALSE的其他值,照样可以;这样,我们的Cls_OnInitDialog就能够正确的返回它的BOOL值了,而Cls_OnCommand在处理之后,也可以 由后面的逗号表达式正确的返回一个TRUE表示消息已处理。
第七重,我们还可以把case也包含进来,就成了如下的样子。
LRESULT CALLBACK Dlg_Proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
chHANDLE_DLGMSG(hwnd, WM_INITDIALOG, Cls_OnInitDialog);
chHANDLE_DLGMSG(hwnd, WM_COMMAND, Cls_OnCommand);
}
return false;
}
chHANDLE_DLGMSG是牛人定义的一个宏,它把case也包含进来了。
#define chHANDLE_DLGMSG(hwnd, message, fn) case (message): return (SetDlgMsgResult(hwnd, uMsg, HANDLE_##message((hwnd), (wParam), (lParam), (fn))))
这个宏要做的就是根据不同的message(##用来连接前后的字符串),把自己“变成”相应的HANDLE_XXXXMESSAGE形式的宏,再通过相应的宏来执行消息处理代码;
比如实际代码中写入:
HANDLE_MSG(hwnd, WM_CREATE, Cls_OnCreate)
则经过转换就变成:
case (WM_CREATE): return HANDLE_WM_CREATE((hwnd), (wParam), (lParam), (Cls_OnCreate))
这样,程序中的语句
switch (uMsg)
{
}
就被翻译成:
switch (uMsg)
{
case (WM_INITDIALOG):
case (WM_SIZE)
case (WM_COMMAND)
}
以上内容转自他人博客,根据以上自己编写了测试程序
工程:
Test12.cpp代码:
#include <windows.h>
#include <windowsx.h>
#include <tchar.h>
#include "resource.h"
/* --------------------宏定义开始------------------- */
#define MsgBox(msg) \
MessageBox(NULL,msg,TEXT("TEST12"),MB_OK)
//引用Jeffrey的定义
// The normal HANDLE_MSG macro in WindowsX.h does not work properly for dialog
// boxes because DlgProc return a BOOL instead of an LRESULT (like
// WndProcs). This chHANDLE_DLGMSG macro corrects the problem:
#define chHANDLE_DLGMSG(hwnd, message, fn) \
case (message): return (SetDlgMsgResult(hwnd, uMsg, \
HANDLE_##message((hwnd), (wParam), (lParam), (fn))))
/* --------------------宏定义结束------------------- */
/* --------------------函数声明开始------------------- */
INT_PTR CALLBACK DlgProc(
HWND hwndDlg, // handle to dialog box
UINT uMsg, // message
WPARAM wParam, // first message parameter
LPARAM lParam // second message parameter
);
BOOL Cls_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam);
void Cls_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify);
/* --------------------函数声明结束------------------- */
/* --------------------函数实现部分------------------- */
int WINAPI _tWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd )
{
HWND hWnd = FindWindow(TEXT("#32770"),TEXT("登录"));
if (IsWindow(hWnd))
{
MsgBox(TEXT("该程序已经在运行"));
}
else
{
DialogBoxParam(hInstance,MAKEINTRESOURCE(IDD_DIALOG1),NULL,(DLGPROC)DlgProc,_ttoi(lpCmdLine));
}
return (0);
}
/* 对话框窗口过程 */
INT_PTR CALLBACK DlgProc(
HWND hwndDlg, // handle to dialog box
UINT uMsg, // message
WPARAM wParam, // first message parameter
LPARAM lParam // second message parameter
)
{
// switch(uMsg)
// {
// case WM_COMMAND:
// return (SetDlgMsgResult(hwndDlg,uMsg,HANDLE_WM_COMMAND(hwndDlg,wParam,lParam,Cls_OnCommand)));
// case WM_INITDIALOG:
// return (SetDlgMsgResult(hwndDlg,uMsg,HANDLE_WM_INITDIALOG(hwndDlg,wParam,lParam,Cls_OnInitDialog)));
// case WM_CLOSE:
// SendMessage(hwndDlg,WM_COMMAND,(WPARAM)MAKEWPARAM(IDC_BTN_EXIT,0),(LPARAM)IDC_BTN_EXIT);
// }
//使用Jeffrey封装的宏简化编码
switch(uMsg)
{
chHANDLE_DLGMSG(hwndDlg,WM_COMMAND,Cls_OnCommand);
chHANDLE_DLGMSG(hwndDlg,WM_INITDIALOG,Cls_OnInitDialog);
case WM_CLOSE:
SendMessage(hwndDlg,WM_COMMAND,(WPARAM)MAKEWPARAM(IDC_BTN_EXIT,0),(LPARAM)IDC_BTN_EXIT);//发送消息[相当于点击了IDC_BTN_EXIT按钮]
}
return (FALSE);
}
BOOL Cls_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam)
{
return (TRUE);
}
void Cls_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
{
switch(id)
{
case IDC_BTN_LOGIN:
TCHAR name[64];
TCHAR password[64];
GetDlgItemText(hwnd,IDC_EDIT_NAME,name,sizeof(name)/sizeof(TCHAR));
GetDlgItemText(hwnd,IDC_EDIT_PASSWORD,password,sizeof(password)/sizeof(TCHAR));
if (lstrcmp(name,TEXT("admin")) | lstrcmp(password,TEXT("admin")))
{
MsgBox("登录失败");
} else
{
MsgBox("登录成功");
}
break;
case IDC_BTN_EXIT:
EndDialog(hwnd,id);
break;
}
}