本文说明两个问题:1.windows的消息处理机制;2.怎么往SetTimer的回调函数传递参数。首先看第一个问题,我们都知道windows是消 息驱动的,windows呈现给用户的任何可以看到听到的东西几乎都是消息驱动的,在底层windows为每个线程准备了一个消息队列,如果用户线程注册 了某个消息,那么在适当的时候windows就会将消息投递到该线程的消息队列中,然后由该线程取出队列中的消息,然后处理之,这个过程有两个参与者,一 个是windows系统,它主要负责投递消息,收不收是用户线程的事,另一个就是用户线程,它主要负责取出消息并处理消息,即使用户线程因为睡眠或者根本就没有设定消息循环,系统还是会投递的,系统和用户线程的消息接口就是消息队列,这就在用户和系统之间关于消息解除了耦合,在用户线程处理消息的时候,其 实还有一个消息队列,因为一个线程不一定只接收一种消息而且不一定马上就能处理完并返回,这个消息队列我们把它叫做消息分发队列或者简称分发队列用来与系统的消息队列区分,注意分发队列里面的消息都是已经格式化后的消息,分发给谁呢?当然是分发给消息的回调函数了,对于有窗口的就是先分发给窗口过程,然后 由窗口过程分发给具体的处理函数。
下面我们来通过一个例子说明一下,用vs2005或VC建立一个Win32工程,然后看自动生成的代码:
int APIENTRY _tWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
...
// 主消息循环:
while (GetMessage(&msg, NULL, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);//msg中按照消息号识别
}
}
return (int) msg.wParam;
}
以上就是消息循环,该线程循环接收消息,然后DispatchMessage消息,Dispatch到窗口过程:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
switch (message)//message就是消息号
{
case WM_COMMAND:
...
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
以上实际上就是windows消息机制的全景,对于windows的timer当然也要套用上面的模式了,在SetTimer调用后,实际上就注册了WM_TIMER消息,以下是函数定义:
UINT_PTR SetTimer(
HWND hWnd,
UINT_PTR nIDEvent,
UINT uElapse,
TIMERPROC lpTimerFunc
);
lpTimerFunc就是回调函数,其形式为:
VOID CALLBACK TimerProc(
HWND hwnd,
UINT uMsg,
UINT_PTR idEvent,
DWORD dwTime
);
SetTimer的参数uElapse就是时间间隔,比如设置为1000即1秒,现在有了一个问题,请看下列代码:
VOID CALLBACK TimerFunc(HWND hwnd,UINT uMsg,UINT_PTR idEvent,DWORD dwTime)
{
Sleep(5000);
}
DWORD CALLBACK AutoBakup( PVOID lpParam )
{
MSG msg;
UINT id=SetTimer(NULL,1,1000,TimerFunc);
BOOL bRet;
while( ((bRet = GetMessage(&msg,NULL,0,0))!= 0) )
{
if(bRet==-1)
{
break;
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
KillTimer(NULL,id);
return 0;
}
竟 然在timer的处理函数中睡眠了,那么SetTimer时的1000毫秒触发一次timer回调还会进行吗?其实不会进行了,本质上那个1000毫秒就 不是说触发回调函数的间隔,而是产生WM_TIMER消息的间隔,因为回调函数中睡眠了,所以也就阻塞了本线程,这个线程就不再往前走了,但是底层的 WM_TIMER消息也会因此而不再投递吗?不会,消息的投递其实不是本线程进行的,而是系统进行的,本线程已经睡眠了,但是系统却没有睡眠,它会继续往该线程的消息队列投递WM_TIMER消息,只不过这些消息不再由该线程的GetMessage取出了,因为它睡眠了(如果对回调函数是否和 GetMessage是否为统一线程有疑义,那么用GetCurrentThreadId()检测一下就好),消息全部堆积在队列里面,然后等待睡眠结束后再一个一个处理。好吧,这个问题解决了,下一个问题又来了,试着将回调函数改为下面的:
VOID CALLBACK TimerFunc(HWND hwnd,UINT uMsg,UINT_PTR idEvent,DWORD dwTime)
{
MessageBoxA(NULL,"zhaoya","msg",MB_OK);
Sleep(5000);
}
发 现完全按照SetTimer时设置的那样,1秒弹出一个消息框,一个一个出来,Sleep好像根本就没有执行,线程根本没有睡眠阻塞,我把 MessageBoxA(NULL,"","",MB_OK)换成printf就不行了,线程立即执行Sleep,这是为何?关键就在 MessageBoxA上,它实际上是一个模态对话框,既然是对话框它就有消息循环,它并没有开独立的线程,因此可以肯定还是原来的线程,原来 MessageBoxA的内部实现了GetMessage-->TranslateMessage-->DispatchMessage的循环,由于还是原来的线程,所以它GetMessage将还包括WM_TIMER,另外还有消息框自己的一些关于界面的消息,比如WM_PAINT,消息框 的出现只是在系统中又注册了一些需要的消息,就是消息框的关于界面事件的消息。WM_TIMER回调函数在那,于是调用之,于是又是一个消息框出来了,但 是一旦你点击最上面的OK键,那么程序立即向下进行,进入Sleep,开始睡眠,所有的消息框好像死掉一般,因为就一个线程,它睡眠了,消息循环不再进行,当然所有消息框的消息循环也不再进行,什么WM_PANIT之类的消息都将阻塞,于是消息框们都和死了一样。
通过以上论述,我想关于windows消息大致已经说清了,下面解决第二个问题,如何向SetTimer的回调函数传递自定义参数。再看TimerProc:
VOID CALLBACK TimerPro(HWND hwnd,UINT uMsg,UINT_PTR idEvent,DWORD dwTime);
看 看MSDN关于第三个参数idEvent的解释,就是Timer的id,我们可以用SetTimer的返回值来作为自定义参数的指针,然后在 TimerProc回调函数中通过idEvent取出,强制转换为自己定义的类型,可是SetTimer的返回值并不是我们所能左右的啊,那么还是要从 MSG结构下手了:
typedef struct tagMSG { // msg
HWND hwnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
DWORD time;
POINT pt;
} MSG;
看 看wParam和lParam,MSDN上说是额外参数,我们只需要重新设置值就可以了,于是我们要取wParam和lParam这两个参数与 TimerProc形参的交集,这个交集就是我们可以自定义的参数,经过测试,发现wParam其实就是SetTimer返回的id,也就是 TimerProc的形参idEvent,于是例子如下:
VOID CALLBACK TimerFunc(HWND hwnd,UINT uMsg,UINT_PTR idEvent,DWORD dwTime)
{
char * buf = (char*)idEvent;
printf( "%s/n", buf );//这里打印的就是"abcde"
}
DWORD CALLBACK AutoBakup( PVOID lpParam )
{
char * buf = "abcde";
MSG msg;
UINT id=SetTimer(NULL,1,1000,TimerFunc);
BOOL bRet;
while( ((bRet = GetMessage(&msg,NULL,0,0))!= 0) )
{
if(bRet==-1)
{
break;
}
else
{
TranslateMessage(&msg);
msg.wParam = (WPARAM)buf;//这里设置参数
DispatchMessage(&msg);
}
}
KillTimer(NULL,id);
return 0;
}
如 果你说,这么把idEvent占了的话,真正的timer的id不就得不到了吗?唉,晕!如果能传递一个指针那么就没有设呢没不能传递了,X位机器上的X 位指针是可以指遍整个虚拟内存的,你可以把自定义参数包装成一个结构,该结构的一个字段指向真正的timer的id,一切...
分享到:
相关推荐
Windows 消息处理机制与 SetTimer 回调函数参数传递 在 Windows 编程中,消息处理机制是了解 Windows 操作系统的关键。Windows 是一种消息驱动的系统,它会将用户的输入、鼠标点击、键盘输入等事件转换为消息,然后...
`GetMessage`函数从消息队列中取出一个消息并将其存储在`MSG`结构中,如果队列中有消息,它会阻塞直到消息可用;`PeekMessage`则不同,即使队列为空也会立即返回,这样可以实现非阻塞的检查消息机制。一旦消息被获取...
3. 如果线程没有窗口(例如,服务线程),则需要自定义消息处理机制,而不是使用`SetTimer`的默认窗口消息处理。 `MyThreadTimer`的实现可能包括以下几个步骤: 1. 创建线程:首先,我们需要创建一个新的线程,确保...
1. **GetMessage**: 这个API函数用于从消息队列中获取下一个消息,而不清除它。在消息循环中,它可以帮助开发者处理消息。 2. **PostMessage**: 用于将消息放入指定窗口的消息队列,而不等待其被处理。这在异步操作...
在VC++2005开发环境中,`SetTimer`函数是Windows API中用于创建和管理定时器的关键函数。定时器允许程序在特定时间间隔后执行某些操作,这对于创建动态用户界面或者实现定时任务非常有用。下面我们将深入探讨`...
`SetTimer`是基于消息驱动的,它会向消息队列中插入一个定时消息WM_TIMER,然后由消息循环来处理这个消息,因此其精度受限于消息循环的调度。这种机制在多数情况下足够使用,但在需要微秒级精度或者频繁触发定时事件...
在Windows操作系统中,消息队列(Message Queue)是窗口间通信的重要机制,它允许应用程序处理来自其他线程或进程的消息。这些消息可以是用户输入,如键盘和鼠标事件,也可以是系统或者其他应用程序发送的通知。本...
在Windows操作系统中,消息机制是窗口程序之间进行通信的核心方式,它构成了图形用户界面(GUI)事件处理的基础。"Windows消息大全"这个资料可能包含了Windows操作系统中所有已知的消息及其详细信息,对于开发者来说...
在Windows编程中,`WM_TIMER`消息是一种非常关键的机制,用于实现应用程序中的定时操作。这个`wm_timer.rar_KillTimer_WM_TIMER_ontimer_settimer`主题涉及到的是如何使用`SetTimer`、`OnTimer`和`KillTimer`这三个...
8. **时间精度**:Windows的SetTimer函数使用的是消息机制,因此其时间精度受限于消息队列的处理速度,可能不是非常精确。如果需要更高精度,可以考虑使用Windows高精度计时器API,如QueryPerformanceCounter和...
#### 二、Windows系统环境下的消息处理机制 在Windows系统中,程序的运行是由一系列消息驱动的。Windows通过消息队列来管理这些消息。当一个程序启动时,Windows首先会调用程序的`WinMain()`函数。在这个函数中,...
总之,利用C++和Windows API编写时钟程序涉及到面向对象编程、Windows消息处理机制、GDI图形绘制等多个方面。通过这个项目,开发者不仅可以巩固C++基础知识,还能加深对Windows程序开发的理解。
Windows API提供了丰富的功能,包括窗口管理、消息处理、图形绘制等,这些都能为动画编程提供基础。 首先,我们需要理解Windows消息机制。在Windows程序中,所有的用户交互,如鼠标点击、键盘输入,都会转化为消息...
在你的OpenGL程序中,你需要处理这个消息并进行渲染操作,然后可能需要再次调用`SetTimer`以启动下一次计时,形成一个循环,使得动画流畅地播放。 在VC++环境下,处理`WM_TIMER`消息通常在窗口消息处理函数如`...
此外,`MessageTimer`可能还提供了错误处理机制,以处理定时器设置失败或系统不支持高精度计时的情况。 为了使用这个`MessageTimer`,开发者需要包含相应的头文件,实例化`MessageTimer`对象,然后调用其启动和停止...
在Windows编程中,消息映射是一种机制,它定义了应用程序窗口如何响应操作系统发送的各种消息。这些消息可能包括用户界面事件,如鼠标点击或键盘输入,或者系统级别的通知。在MFC(Microsoft Foundation Classes)库...
在Visual C++编程中,消息处理是构建用户界面和交互逻辑的核心部分。第五章“消息处理与...在后续的学习中,深入理解消息机制和熟练运用ClassWizard将有助于提升编程能力,更好地实现Windows平台的软件开发。
在VC++编程中,消息映射与处理是核心机制之一,它使得程序能够响应各种用户交互和系统事件。本文将详细探讨如何利用消息映射和处理来创建一个使用定时器显示毫秒级时间的程序。 首先,理解消息映射(Message ...
- **Windows处理机制**: - **Windows98**:定时器的分辨率为55毫秒,即每秒约18.2次中断。 - **Windows NT**:定时器的分辨率为10毫秒。 - 应用程序无需直接处理硬件中断,而是通过消息队列接收`WM_TIMER`消息。...
开发者可以使用`SetTimer`函数设置定时器,参数包括定时器ID、间隔时间和消息处理函数。当定时时间到,系统会将WM_TIMER消息放入消息队列,等待应用程序处理。 1.2 使用步骤: 1. 创建一个窗口类,并注册。 2. 在...