1.4 消息循环和回调函数

来源:互联网 发布:mysql 一行拆分多行 编辑:程序博客网 时间:2024/05/07 17:35
**************************************************
*  本文由小鸟飞飞整理发表 <samboy@sohu.com>   *
* 首发网站:蓝丽网               *
*  其他网站转载请保留以上信息,谢谢!      *
**************************************************

上 一节的程序可以运行了吧。现在介绍一下消息循环和回调函数

消息循环

通常的消息循环代码如下:
MSG msg;
while(GetMessage(&msg,NULL,0,0))
{
 TranslateMessage(&msg);
 DispatchMessage(&msg);
}

GetMessage 函数从应用程序消息队列中取走一条消息,该函数的原型如下:
BOOL GetMessage(
LPMSG lpMsg,     // address of structure with message
HWND hWnd,      // handle of window
UINT wMsgFilterMin, // first message
UINT wMsgFilterMax  // last message
);
参数lpMsg是接受消息的变量的指针。
参数hWnd指定了接收属于哪个窗口的消息。
参 数wMsgFilterMin,wMsgFilterMax指定了接受某一范围内的消息。

  如果队列中没有满足条件的消息,该函数将一 直等待,不会返回。除了WM_QUIT消息外,该函数返回非零值,对WM_QUIT消息,该函数返回零。也就是说,只有收到WM_QUIT消息,上述代码 才能退出while循环,程序才有可能结束运行。

  TranslateMessage函数对取道的消息进行转换。用户按动一下某个键, 系统将发出WM_KEYDOWN,WM_KEYUP,并且参数中提供的是该键的虚拟扫描码。但有时用户按动一下某个键,我们想得到一条表示用户输入了某个 字符的消息,并在消息补充参数中提供字符的编码。TranslateMessage能将可行的WM_KEYDOWN, WM_KEYUP消息对转换成一条WM_CHAR消息,将可行的WM_SYSKEYDOWN, or WM_SYSKEYUP消息对转换成一条WM_SYSCHAR消息,并将转换后得到的新消息投递到程序的消息队列中。转换过程不会影响原来的消息,只在消 息队列中增加新消息。

  DispatchMessage函数将取道的消息传递到窗口的回调函数中去处理。可以理解成该函数通知操作系 统,让操作系统去调用窗口的回调函数来处理收到的消息。

  所有Windows程序的消息处理代码基本上都是相同的,如没特殊需要,可以 照搬照抄上述代码。

  顺便提示:从队列中取消息还有PeekMessage函数,PeekMessage函数有两种取消息的方式,第一 种与GetMessage一样,从队列中直接取走消息,第二种是只取消息的一个副本,并不将消息从队列中取走。无论哪种方式,PeekMessage都不 会因队列中没有满足条件的消息而阻塞,当取到满足条件的消息,该函数返回非零值反之,返回零。向队列中发送消息有PostMessage和 SendMessage,PostMessage函数发送消息后立即返回,而SendMessage需等到发送的消息被处理完后才能返回。还有一个 PostThreadMessage用于向线程发送消息,关于线程,请在以后的章节再学,但我们也因此想到消息不一定总是与窗口相关的,也就是说,对于某 些消息,其对应的MSG结构中的hwnd可以为NULL。

回调函数

回调函数的原型为:
LRESULT CALLBACK WindowProc(
HWND hwnd,   // handle to window
UINT uMsg,   // message identifier
WPARAM wParam, // first message parameter
LPARAM lParam  // second message parameter
);
我们可以将函数名WindowProc改 为我们喜欢的名称,如MyProc,该函数的四个参数对应消息的窗口句柄,消息码,消息码的两个补充参数。
在该函数内部是一个庞大的 switch语句,用于对各种感兴趣的消息进行处理。我们分析程序中的代码。
LRESULT CALLBACK MyProc( HWND hWnd,UINT Msg,WPARAM wParam, LPARAM lParam)
{
HDC hDC;
switch(Msg)
{
case WM_CHAR:
char str[20];
sprintf(str,"the char code is %d",wParam);
MessageBox(hWnd,str,"Message",MB_OKCANCEL);
break;
case WM_LBUTTONDOWN:
MessageBox(hWnd,"mouse click","Message",MB_OK);
hDC=GetDC(hWnd);
TextOut(hDC,LOWORD(lParam),HIWORD(lParam),
  "Mouse Click",strlen("Mouse Click"));
ReleaseDC(hWnd,hDC);
break;
case WM_CLOSE:
if(IDOK==MessageBox(NULL,"真的要要退出吗?",
"Message",MB_OKCANCEL|MB_ICONQUESTION))
{
DestroyWindow(hWnd);
}
break;
case WM_PAINT:
PAINTSTRUCT ps;
hDC=BeginPaint(hWnd,&ps);//在 WM_PAINT里必须用这个函数
TextOut(hDC,0,0,"Output message",strlen("Output message"));
EndPaint(hWnd,&ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return(DefWindowProc(hWnd,Msg, wParam,lParam ));
}
return (0);
}

  当用户在窗口上按下一个字符键,程序将得 到一条WM_CHAR消息,参看msdn,在其wParam参数中含有字符的码值。使用程序中的代码,我们可以随时获得某个字符的码值,不用为此去专门查 找书籍。MessageBox函数可以弹出一个显示信息的对话框,如果我们按下BackSpace键,在这里将弹出"the char code is 8"消息。

  当用户在窗口上按下鼠标左按钮时,程序弹出一条消息框对收到的WM_LBUTTONDOWN进行响应,以证明按下鼠标左按 钮动作与WM_LBUTTONDOWN消息的这种对应关系,另外程序还将在窗口上鼠标所按下的位置写上一串文字。当我们要在窗口上写字、绘图,并不直接在 这些设备上操作,而是在一个称为设备描述表(Device Contexts,简称DC)的资源上操作的。使用DC,程序不用为图形的显示与打印输出分别单独处理了。无论是打印还是显示,我们都是在DC上操作,然 后由DC映射到这些设备上。使用DC,我们不用担心程序在不同的硬件上执行的差异性,DC能够为我们装载合适的设备驱动程序,实现程序的图形操作与底层设 备无关。

  GetDC函数能够返回同窗口相关连的DC的句柄。

  TextOut函数在当前DC上指定的位置输出一个 字符串,当按下鼠标左按钮,消息WM_LBUTTONDOWN的补充参数lParam中含有鼠标位置的x,y坐标,其中的低16位含有x坐标,高16位含 有y坐标,可以分别通过LOWORD,HIWORD宏获得。

  对于每次成功调用GetDC返回的DC句柄,在执行完所有的图形操作后, 必须调用ReleaseDC释放该句柄所占用的资源。否则,会造成内存泄露。我曾经帮助一个叫王健的学员调试他的On-job项目程序,他发现他的程序所 占用的内存总是以4k的增量向上增长,每运行几小时后,程序便因内存不足而崩溃了。后来发现就是因为程序在定时器中反复使用GetDC而没有使用 ReleaseDC释放造成的。读者可以将例子程序中的ReleaseDC一句注释掉,编译后运行,在NT4.0/win2000下启动任务管理器,切换 到进程标签,查看你的程序所使用的内存量,在程序中不断点击鼠标左按钮,程序将不断调用GetDC函数,你将发现你的程序占用的内存量不断向上增长,我们 通常使用这样的方式来检测程序的内存泄露的。

  当用户点击窗口上的关闭按钮时,系统将给应用程序发送一条WM_CLOSE消息,如果程 序不做进一步处理,窗口是不会关闭的。我们在程序中利用一个选择对话框,如果用户确认真的要退出,程序调用DestroyWindow函数将窗口关闭。

   窗口关闭后,系统将给应用程序发送一条WM_DESTROY消息,需要注意的是,主窗口的关闭,不代表应用程序结束,在WinMain函数中的消息循环 代码中,GetMessage函数必须取到一条WM_QUIT,消息循环才能结束。要让程序正常退出,必须在WM_DESTROY消息响应代码中,调用 PostQuitMessage函数向程序的消息队列中发送一条WM_QUIT消息,PostQuitMessage函数的参数值传递给WM_QUIT消 息的wParam参数,通常用作WinMain函数的返回值。

  当窗口第一次产生,移动,改变大小,从其他窗口后面切换到前面等情况都 会导致窗口的重画。重画时将使用设计窗口类时指定的刷子粉刷窗口的背景,窗口上原有的文字和图形都将被擦除掉。要想让图形和文字总显示在窗口的表面,只能 是在这些图形和文字被擦除后,立即又将它们画上去。这个过程对用户来说,是感觉不到的,他们只能感觉到这些图形和文字永远都和窗口一并存在。当系统粉刷完 窗口的背景后,都会发送一条WM_PAINT消息,以便通知应用程序原有的图形和文字已被擦除,如果还想保留哪些图形和文字,请在此处加入处理代码。也就 是说,我们在WM_PAINT消息响应中作出的图形和文字是“永远”存在的。对于WM_PAINT消息响应代码中要获得窗口的DC,只能使用 BeginPaint函数,除此之外的消息响应代码中必须用GetDC获得窗口的DC,BeginPaint获得的DC最后必须用EndPaint释放。

   提醒:水平或垂直改变窗口的大小时,窗口是否重画,取决于WNDCLASS结构中style成员的设置中是否包含CS_VREDRAW与 CS_HREDRAW。

  DefWindowProc函数提供了对所有消息的缺省处理方式,对于大多数不想特殊处理的消息,程序都可以 调用这个函数来处理,所以程序在switch的default语句中调用此函数进行处理。
0 0