分享
 
 
 

No MFC 编程06 - 检测来自系统的消息

王朝vc·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

( 之前的一篇教程如果有什么不明白,不要紧,请继续往下走,以后回过头看就自然不会再迷糊! )

Windows 用的是基于事件驱动的编程方法,所以检测并处理消息是很重要的。现在让我们继续研究一下消息队列 (Queue) :

一个更加生动的例子! 系统 和 我 之间隔着一条河,系统在上游,我则处在下游并设置了一个水闸。 系统有什么要通知我就写到瓶子里,扔进河中。 最后这些瓶子在我设的水闸前排起队来了。 (这个例子不错嘛) 我有空的时候就捞起一个瓶子,读出里面的指示,接着就知道下一步要做些什么了。

如果我没有空怎么办? 那么你所处理的消息有可能已经错过了最佳时机。 有一个办法可以让你的消息插队,那就是用 PostMessage() 。

好了,先来动动手,模仿 系统 给我的程序的 消息队列 发送消息。

要用到的函数: 发送消息到队列 - PostThreadMessage() , 检索队列中消息 - PeekMessage() ,还有一个装载消息和相关数据的结构 MSG (也就是装指令的瓶子),具体如下:

typedef struct tagMSG {

HWND hwnd; // 与该条消息相关的窗口句柄,为 0 则没有相关的窗口

UINT message; // 消息的具体数值,相当于没符号的 int (16位),范围是 0 — 65535

WPARAM wParam; // 第一个相关参数,类型同上。

LPARAM lParam; // 第二个相关参数,相当于 long 定义,范围 (略)

DWORD time; // 系统计时,相当于 unsigned long ,用 GetTickCount() 可以取得相同的值

POINT pt; // 鼠标现时的坐标,相当于 ( LONG x; LONG y; ) 坐标

} MSG;

具体的源程序见下面:

// File Name: WinMain.cpp

#define WIN32_LEAN_AND_MEAN // Say No to MFC !!

#include <windows.h>

char Temp[177] = "Hello world"; // 临时字符串变量

char Result[512] = ""; // 用于保存、输出结果

char Title[] = "Sample for Message details __CopyRight - `海风 `";

// Name: WinMain() // 主程序入口

// ------ ---------- ----------- ---------

int WINAPI WinMain( HINSTANCE hInstance,

HINSTANCE hPrevInstance,

LPSTR lpCmdLine, int nCmdShow )

{

MSG msg; // 定义了一个装载消息的结构

DWORD CurThreadID = GetCurrentThreadId( ); // 取得当前线程的 ID (标志号)

MessageBox( NULL, " 请按 确定 开始测试!", Title, MB_OK | MB_TOPMOST );

BOOL done = false; // 用来保存 发送消息 是否成功

// 发送消息需用线程的 ID 指示要送到哪个消息队列中去,ID 由 GetCurrentThreadId() 取得

done = PostThreadMessage( CurThreadID, 123 , 1, 22 );

if (! done ) MessageBox( NULL, "发送消息失败_1!", Title, MB_OK | MB_TOPMOST );

done = PostThreadMessage( CurThreadID, 345 , 2, 22 );

if (! done ) MessageBox( NULL, "发送消息失败_2!", Title, MB_OK | MB_TOPMOST );

done = PostThreadMessage( CurThreadID, 4321 , 3, 22 );

if (! done ) MessageBox( NULL, "发送消息失败_3!", Title, MB_OK | MB_TOPMOST );

// 以下这个发送消息的效果相当于 PostQuitMessage( 12 );

done = PostThreadMessage( CurThreadID, WM_QUIT , 12, 0 );

if (! done ) MessageBox( NULL, "发送消息失败_4!", Title, MB_OK | MB_TOPMOST );

PostQuitMessage( 12 ); // 发送退出消息

// 相当于发送了 WM_QUIT 消息,但只有 GetMessage() 会检测它,PeekMessage() 则要外加判断来处理。

// WM_QUIT 的值等于 18 (即0x0012),点击鼠标右键看 "Go To Definition Of WM_QUIT"

long i = 24, k = 0; // i 为循环系数初始值, k 用来纪录接受了多少条消息

while (i) // 循环系数 i 为 0 则退出

{

if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))

{

wsprintf( Temp , "检测到 第 %ld 号 消息 \n "

" 第一参数 wParam = %ld ;"

" 第二参数 lParam = %ld ;\n "

" (系统时间) msg.time = %ld ; "

" (涉及的句柄) msg.hwnd = %ld \n\n",

msg.message, msg.wParam, msg.lParam, msg.time, msg.hwnd );

lstrcat( Result, Temp ); k++; // 追加到结果字符串后面

}

i--; // 循环系数减少 1,为 0 则退出

}

wsprintf( Temp , "共收到 %ld 条消息\n", k ); // 整理报告数字

lstrcat( Result, Temp );

MessageBox( NULL, Result, Title, MB_OK | MB_TOPMOST ); // 显示检测到的消息详细情况

ExitProcess(0);

return NULL;

}

以上这个源程序要注意两个地方:

其一是 PeekMessage() 的最后一个参数 PM_REMOVE ,如果换成 PM_NOREMOVE ,则虽然取得了消息,但仍保留一份在消息队列里面 (MSDN 原文:Messages are not removed from the queue after processing by PeekMessage. ),会有什么效果?你自己换一下参数试一下吧。

其二是 如果使用 PostQuitMessage() 函数,其实际效果相当于用 PostThreadMessage() 向你的线程发送 WM_QUIT 消息,你可以在消息泵里判断 如果 ( msg.message == WM_QUIT ) 为真就退出 消息(处理)循环 (使用 break ) 。

另外,在上面的例子中改用 PM_NOREMOVE 来测试,如果得不到预期的结果,我一点都不觉得奇怪,建议采用下面的方法来显示结果。

while (true) // 清除所有多余消息

{ if(! PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) break; }

MessageBox( NULL, Result, Title, MB_OK | MB_TOPMOST ); // 显示检测到的消息详细情况

下面我们将继续尝试检测真正来自系统给我们的消息!

不过在此之前,我们需要先简单介绍一下句柄,因为一会儿要创建的新窗口就有一个。

句柄是翻译过来的名词 (英文是 Handle ) ,可是它一点都不好帮助理解!

我的一个朋友发表了他的看法 " 句柄就好像一个苹果的小柄,如果你要吃苹果... " 另一个朋友打断了他 " 什么嘛,应该是这样!你做了亏心事,让我拿住了把柄,嘿嘿嘿,我要你做什么你都要听我的... "

对! 我看就是这么回事,比方说创建了一个窗口, 你要知道它的状态,大小,位置等,你要有句柄; 你要改变它的属性,改变它的式样,你也要有句柄才行; 就算你要毁了它,一样要有它的句柄才成事。 既然句柄那么重要,那么什么时候可以得到句柄呢?

一般来说,当你使用 CreateWindowEx() 函数创建一个新窗口的时候,函数会返回一个,记得要保存它!

( 一个提示: 句柄并不是只是窗口才有的,许多 windows 里面的对象都用句柄来访问, 比如你向系统申请内存, 就要有一个堆的句柄 (handle of heap) 等等,与句柄相关的内容以后还会提到。 )

接着我们本篇的主题 - 检测来自系统的消息 。

( 以下例子使用 CreateWiondowEx() 函数创建一个新窗口,关于该函数具体用法以后会作详细说明 )

在开始之前,我们要先大概有一个印象,系统会发什么事件给我们? 什么时候会发给我们?

先回答后面一个问题,一般来说,当我们的程序获得输入焦点,也就是程序的窗口处于最上面,且标题变成蓝色底色,系统会把输入信息发给我们的程序来处理。

会有些什么样的输入信息呢?大概是这些,比方说,我们按动了键盘,当我们按下键盘上一个键,系统会向 程序(当前输入焦点) 发送一个 WM_KEYDOWN 的信息,(WM_KEYDOWN == 256),而我们松开键的时候系统会发出 WM_KEYUP (WM_KEYUP == 257,通过查看16位的 msg.wParam 可以区分按下的是哪一个键 ),类似的还有 WM_MOUSEMOVE 和 WM_LBUTTONDOWN 等与鼠标输入有关的信息。

在下面的例子中,在按下 确定 继续以后,你有三秒时间令系统向你的程序发送输入信息!

// File Name: WinMain.cpp

#define WIN32_LEAN_AND_MEAN // Say No to MFC !!

#include <windows.h>

char Temp[177] = "Hello world";

char Result[1024] = ""; // 用于保存、输出结果

char Title[] = "Sample for Message details __CopyRight - `海风 ...............";

// Name: WinMain() // 主程序入口

// ------ ---------- ----------- ---------

int WINAPI WinMain( HINSTANCE hInstance,

HINSTANCE hPrevInstance,

LPSTR lpCmdLine, int nCmdShow )

{

MSG msg; // 定义了一个装载消息的结构

//*

// 下面用 CreateWindowEx() 函数创建一个临时窗口 , 使用 hWnd 暂时保存句柄

HWND hWnd=CreateWindowEx( WS_EX_TOPMOST | WS_EX_TOOLWINDOW ,

"Edit","1231",

WS_OVERLAPPEDWINDOW,

120,120,

280,180,

NULL,NULL,hInstance,NULL);

ShowWindow( hWnd, SW_SHOW ); // 让这个窗口可以被看见

MessageBox( NULL, "创建了测试窗口,按 确定 继续测试!", Title, MB_OK | MB_TOPMOST );

// */

Sleep(3000); // 利用这段时间在新窗口上触发一些如 鼠标移动,按下键钮 等事件

// 先发送三条自定义消息

BOOL done = false; // 用来保存 发送 是否成功

// 当前线程的 ID 由 GetCurrentThreadId() 取得

done = PostThreadMessage( GetCurrentThreadId(), 123 , 1, 22 );

if (! done ) MessageBox( NULL, "发送消息失败_1!", Title, MB_OK | MB_TOPMOST );

// PostMessage() 发送的是与窗口相关的消息

done = PostMessage( hWnd, WM_KEYDOWN , 55, 4321 ); // 所发送的是假的 WM_KEYDOWN 消息

if (! done ) MessageBox( NULL, "发送消息失败_2!", Title, MB_OK | MB_TOPMOST );

PostQuitMessage( true ); // 发送退出消息

// 只有 GetMessage() 可以检测它,PeekMessage() 要外加判断

// 以下开始检测系统消息

long i = 24, k = 0; // i 为循环系数初始值, k 用来纪录接受了多少条消息

while (i) // 循环系数 i 为 0 则退出

{

if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))

{

wsprintf( Temp , "检测到 第 %ld 号 消息 \n "

" 第一参数 wParam = %ld ;"

" 第二参数 lParam = %ld ;\n "

" (系统时间) msg.time = %ld ;"

" (涉及的句柄) msg.hwnd = %ld \n\n",

msg.message, msg.wParam, msg.lParam, msg.time, msg.hwnd );

lstrcat( Result, Temp ); k++; // 追加到结果字符串后面

}

i--; // 循环系数减少 1,为 0 则退出

}

wsprintf( Temp , "共收到 %ld 条消息\n", k ); // 整理报告数字

lstrcat( Result, Temp );

DestroyWindow( hWnd ); // 退出程序前要销毁窗口

MessageBox( NULL, Result, Title, MB_OK | MB_TOPMOST ); // 显示截获消息的详细内容

// 请细心的读者比较一下消息的系统时间,是不是有插队的情况发生呢?

ExitProcess(0);

return NULL;

}

在以上实验中,如果曾按下键钮,应该会收到 WM_KEYDOWN (第 256 号消息),和 WM_KEYUP (第 257 号消息),它们会成对的出现。

不过,出现了 第 280 号 消息( msg.message == 0x0118 ),我搞不懂那是什么消息来着。

…… `海风 2002年10月17日 am 2:01

——————————————————————————

学习的秘诀是要知道 Why ,而不仅仅是 How ...

目前喜欢的歌: 動力火車 - 忠孝东路走九遍

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有