一、WinMain函数的优化
1、windows程序两个非常重要的函数:WinMain函数和WinProc函数。
WinMain函数作为Windows程序的入口点函数,主要完成以下功能:设计窗口类别、注册窗口类、创建窗口、显示窗口、更新窗口、消息循环。
WinProc是回调函数,由开发者自己实现,它是程序运行的中心,是窗口的生命中枢,主要实现区分不同消息,做出不同回应。
2、对WinMain函数的优化
int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdline, int nCmdShow)
{
if(!hPrevInstance)
{
if(!InitApplication(hInstance))
return false;
}
if(!InitInstance(hInstance, nCmdShow))
return false;
...
}
//---------------------------------------------------------------------------------------
BOOL InitApplication(HINSTANCE hInstance)
{
WNDCLASS wc;
....//填充窗口类的成员
return(RegisterClass(&wc));
}
//----------------------------------------------------------------------------------------
BOOL InitInstance(HINSTANCE int nCmdShow)
{
_hwnd = CreateWindow(...);
...
}
3、实际上MFC就是这样做的,为什么要用这种方式?
在Windows3.x时代,所有进程共用同一个地址空间,同一个窗口类别只需要注册一次,即可以供同一个程序后续的每一个执行实例(Instance)使用,WinMain的传人参数hPreInstance可以用来判断本次进程是否是该程序的第一个运行实例(hPreInstance是前一个运行实例的句柄,如果没有传入0),所以InitApplication函数只能被应用程序的第一个实例调用,在其中完成窗口类的设计和注册是非常合适的。
但是,在WindowsNT和Windows95之后,情况发生了变化,不同进程不再共用地址空间,每一个进程都有独立的地址空间,共享窗口类别已经不可能了,因此注册窗口类在每一个实例中都要执行,值得注意的是,Win32系统令hPreInstance永远为0,所以以上设计并不会带来问题,即符合了新环境的要求,又兼顾到旧代码的兼容,所以一直延用至今。
4、注:MFC将上述的InitApplication函数和InitInstance函数封装为CWinApp类的两个虚拟成员方法。
二、消息循环的优化
1、消息循环的原型
while(GetMessage(&msg)) {
TranslateMessage(&msg);//转换键盘消息
DispatchMessage(&msg);//分派消息
}
DispatchMessage函数,没有指定函数名称,就把消息送给了窗口过程函数,它是怎么做到的?
因为:消息发生之时,操作系统已经根据当时状态,为消息标明了所属的窗口,而且窗口所属的窗口类别又已经标明了窗口函数(在设计窗口类别的时候),所以DispatchMessage再经过USER模块的协助,就可把消息送到窗口函数手中。
2、GetMessage的由来,为什么消息循环要使用GetMessage?
这里先讲一下,非抢占式多任务,在以前的Windows系统中,多任务方式是“协作式多任务”,意思是说,一个任务得到CPU时间后,除非它自己放弃使用CPU,否则将完全霸占CPU,所以任务之间需要协作——使用一段时间的CPU,然后程序主动放弃使用,供其他程序使用(其他程序也如此),这样才能保证系统的正常运行。
而消息循环中的GetMessage就是这种“非抢占式多任务”的实现关键,应用程序藉由此动作,提供了释放控制权的机会:如果消息队列中没有我的消息,我就把机会让给别人。许多资料里说GetMessage函数的特点是:”直到从消息队列中取得一个有效消息,才返回“,就是这个原因,当消息队列中没有该窗口的消息时,实际上是系统让该程序实例挂起,运行其他程序去了,等到队列中再次出现该窗口的消息并且其他程序释放了控制权,系统再把控制权交给该程序,让它继续运行。
然而,WindowsNT以后,Windows系统具备强制性(preemptive)多任务能力,不再非靠GetMessage释放CPU控制权不可,但是,因为应用程序仍然需要靠消息驱动,还要抓取消息,因此程序写法仍不变。
3、空闲时间的利用
所谓空闲时间(idle time),是指”系统中没有任何消息等待处理“的时间,实际上计算机的空闲时间是非常多的。”背景工作“如屏保程序,最适合在空闲时间完成,传统的SDK程序如果要处理空闲时间,可以以以下的循环代替WinMain的传统消息循环。
while(true)
{
if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if(msg.message == WM_QUIT)
break;
TranslateMssage(&msg);
DispatchMessage(&msg);
}
else
{
OnIdle();//空闲时间处理函数
}
}
4、PeekMessage与GetMessage的比较
它们都是从消息队列中抓取消息,如果抓不到,程序的主执行线程(primary thread,是一个UI执行线程)会被操作系统”虚悬住“,当操作系统再次回来照顾此一执行线程时,而发现消息队列中仍然为空,这时候两个API函数的行为就有不同了:
GetMessage会过门不入,于是操作系统再去照顾其他线程;
PeekMessage会取回控制权,使程序得以执行一段时间,于是上述消息循环进入都OnIdle函数中执行。