沧桑不尽
无穷的沧桑,不尽的落叶
1.5 在窗口上写上“Hello World”
 再次声明:高手莫看,浪费时间
这一节我们乘胜追击,来继续深入学习下,学习窗口处理时间的东东。
   也许你以前听说过,windows系统是消息驱动的,可是可能根本就不知道什么消息,更不知道什么消息驱动了。
其实什么是消息呢,说白了就是我们点鼠标击键盘而程序发生反应,消息是一种数据,就是我们点鼠标击键盘后,系统把我们的操作封装到数据中,
然后发送给程序,让程序对我们点鼠标击键盘的动作做出反应,当然程序也可以置之不理。Windows可是一个多任务的系统,
而且同时可能产生很多的击键动作,那么同时可能能会有很多消息,windows系统为了更好的管理维护这些消息,
就把这些消息加入消息队列中,消息队列其实就是消息的集合。
学过VB的人知道,VB中的程序是事件驱动的,因为一般都是发生时,调用相应的事件处理函数,所以整个处理过程都好像是事件引发的一样。
这里的事件就是指我们击键的动作等。
学过JAVA的人知道,JAVA中有事件适配器,来捕获相应的事件,并交给相应的处理方法进行处理。
其实三种语言的处理过程也都是大同小异,只不过JAVA和VB把这些处理过程给封装了,VB尤其封装的更厉害,所以编程者不必考虑和知道这中间的细节问题,
仍然可以编写出实用的程序,但正是由于细节的原因,用VB的开发的程序并不能高效地处理问题。
而C语言本身就是面向过程的语言,所以这一过程可以用C语言更好地表现出来,这也是我用C而不用C++的原因之一。
通过前几节的学习,我们知道了,在窗口程序中都有一个处理窗口的函数,其实所有的消息将会得到怎样的处理,都是此函数安排的。
现在大家再回去看看那个程序代码和注释,相信应该能明白些了吧。
系统产生的消息是不断的,但是中间是有间隔的,程序要想知道有没有自己的消息,得不停地去问系统,问系统当前有没有属于自己的消息,
这就需要一个循环来实现了。
下面先看下前两节种用到的消息循环代码:
     while(GetMessage(&Msg,NULL,0,0))
    {
        TranslateMessage(&Msg);//翻译消息
        DispatchMessage(&Msg);//分派消息
    }
Windows为消息定义一种新的数据类型MSG,用于保存消息的相关信息。在windows中GetMessage函数从消息队列种取得消息,
填写好MSG结构并返回,如果获取的消息是WM_QUIT消息,则退出循环。
TranslateMessage将MSG结构传给Windows进行一些键盘消息的转换,当有键盘按下或者放开时,Windows产生WM_KEYDOWN
和WM_KEYUP或WM_SYSKEYDOWN和WM_SYSKEYUP消息(像WM_KEYDOWN这些都是微软定义的一些宏,是什么意思,看字面意思就可以知道了),
但是这些消息的参数种包含的是按键的扫描码(暂时不用理会),转换成常用的ASCII码要经过查表,很不方便,TranslatMessage遇到键盘消息
则将扫描码转换成ASCII码并在消息队列种插入WM_CHAR或WM_SYSCHAR消息,参数就是转换好的ASCII码,如此一来,要处理键盘消息的话只要是
处理WM_CHAR消息就好了。非键盘消息TranslateMessage则不做处理。
最后,由DispatchMessage将消息发送到窗口对应的窗口过程去处理。窗口过程返回后DispatchMessage函数才返回,然后开始新一轮的消息循环。
想想我们这节的目的是为了在洁白的窗口种写下“Hello World”,那么我们怎么来留下我们的笔迹呢?窗口我们是能做出来了,那么怎么在上面写东西呢,
等等,在上面写东西的前提是不是窗口做出来之后,当初我是这么想的,后来看到别人的代码才知道原来可以在窗口绘制的过程就绘制“Hello world”了。
Windows有时真是个细心的家伙,把窗口创建到显示的一瞬间又给划分了很多小的过程。在绘制窗口时,Windows会产生WM_PAINT消息,
那么我们在得到这个个消息的时候,来留下我们的笔迹,岂不就是下手最早的时刻。其实Windows在屏幕上输出文字和图像是一样的,都是在屏幕上画,
和我们在纸上画图和写字是一样的,都是用笔来画的,只不过用的笔不一样而已,如果牵强用一支笔去做所有的工作,效果并不会理想的。Windows的笔也是这样的,
不过这些笔是函数而已,画图和画文字的函数不一样而已。
下面就接着上节修改的代码继续修改,必要的注释和改变的地方我会标明的
#include <windows.h>
 
#define CUR 0x1000         //预定义光标的id
 
HDC hDC;//HDC是指设备上下文(暂时不用管,只要能这样用就可以了)的句柄
//PAINTSTRUCT要绘制的信息,详情请登陆http://msdn.microsoft.com/en-us/library/dd162768(VS.85).aspx
//了解下就可以了,没什么重要的东西
PAINTSTRUCT paint;
RECT rect;//RECT用来存储窗口信息的结构,只要是窗口的坐标、宽度和高度。
 
 
//回调函数
LRESULT WINAPI WinProc(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam)
{
    
switch(Msg)//处理消息过程,什么是消息,下节再讲
    {
     
case WM_PAINT:
     
//BginPaint做些绘画的开始工作,填充PAINSTURCT结构,返回设备上下文(暂时不用理解)句柄
     hDC=BeginPaint(hWnd,&paint);
     
//GetClientRect用来获取窗口所在客户区的位置大小信息
     GetClientRect(hWnd,&rect);
     
//DrawText就是Windows用来“画字”的笔了,DT_*之类是指文字的样式,看字面意思也能看懂的
     
//有多少样式呢,可以查看这里http://msdn.microsoft.com/en-us/library/ms901121.aspx
     
//本例中是单线、水平居中和竖直居中。
     DrawText(hDC,"Hello World!",-1,&rect,DT_SINGLELINE|DT_CENTER|DT_VCENTER);
     
//EndPaint就是做些收尾的工作了。
     EndPaint(hWnd,&paint);
     
break;
    
case WM_DESTROY://响应鼠标单击关闭按钮事件
        PostQuitMessage(0);//退出消息队列,至于什么是消息队列,下节说
        return 0;//退出函数
    }
    
return DefWindowProc(hWnd,Msg,wParam,lParam);
}
 
//主函数
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nShowCmd)
{
    
char *cName = "myWindow";
    WNDCLASSEX wc;
    HWND hWnd;
    MSG Msg;
 
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);//通过函数来设置一个白色的背景,这里大家设置为NULL看看,会很有趣的
    wc.hCursor = LoadCursor(hInstance,MAKEINTRESOURCE(CUR));//这里改了,来载入光标资源
    wc.hIcon = NULL;//不设置
    wc.hIconSm = NULL;//不设置
    wc.hInstance = hInstance;//当前程序的句柄,hInstance是有系统给传递的
    wc.lpfnWndProc = WinProc;//窗口处理过程的回调函数。
    wc.lpszClassName =(LPSTR)cName;//窗口类的名字。
    wc.lpszMenuName = NULL;//目录名,不设置
    wc.style = CS_HREDRAW | CS_VREDRAW; 
 
    RegisterClassEx(&wc);//在系统中注册
 
    hWnd = CreateWindowEx(WS_EX_CLIENTEDGE,cName,"我的窗口我喜欢",WS_OVERLAPPEDWINDOW,
        
200,100,600,400,NULL,NULL,hInstance,NULL);//创建窗口,窗口标题为"我的窗口我喜欢"
    if(hWnd == NULL)
    {//容错处理
        MessageBox(NULL,"There's an Error","Error Title",MB_ICONEXCLAMATION|MB_OK);
        
return 0;
    }
    ShowWindow(hWnd,nShowCmd);//显示窗口
    UpdateWindow(hWnd);
 
    
//下面是对消息的循环处理,大家先不必管这些,下节课我会细说的
     while(GetMessage(&Msg,NULL,0,0))
    {
        TranslateMessage(&Msg);//翻译消息
        DispatchMessage(&Msg);//分派消息
    }
    
return Msg.message;
}

 

最终运行结果:

 

其实就是比原来的多了三个变量和几句代码,多的我也标出来了,而且都说明,那些简单的函数,大家可以自己查下,很简单的,
我就不再为一些简单的函数来打字了,这样也可以锻炼大家的动手能力。
编译连接后,大家看看预期的结果出现了吧,洁白的窗口上留下了我们的字迹。建议学过VBJAVA的读者,可以联系起来想一想,
C的处理消息过程给理解下,理解下消息的结构和概念,熟悉西Windows的消息机制,这样就可以为以后编写优质的软件打下坚实的基础。
此言不虚的,像金山词霸的屏幕取词功能就是对Windows消息巧妙的运用;键盘记录器(木马)也是利用了截获Windows消息,
而记录我们的按键行为,从而盗取信息的。大家好好理解下本节内容,自己动手写点东西,查些其他的事件信息,改进下程序,多熟练下,
为后面的学习做一点铺垫。

 

 

posted on 2009-12-26 18:37  沧桑不尽  阅读(3110)  评论(14编辑  收藏  举报