第伍章 Windows图形界面-上

上学《Windows程序设计》记录

 

概念贴士:

1.  每一个GUI应用程序至少应该成立二个窗口,称为主窗口,它看成用户与应用程序间的主界面来提供服务。半数以上应用程序也直接或直接地创立别的窗口,来形成与主窗口相关的工作。

 

2.  应用程序的主窗口包罗标题栏、菜单栏、Windows系统菜单、最小化按钮、最大化按钮、回复按钮、关闭按钮、可转移大小的边框、窗口客户区、垂直滚动条和水平滚动条。

 

3.  VC++陆.0创制GUI程序:  一)创制VC++陆.0,采纳命令“文件”,点击“新建”;

                2)选择“工程”,点击“Win32
Application”选项;

                三)输入工程名、存放路径;

                四)点击OK按钮,选用第1项“三个简约Win3二应用程序”;

                伍)点击“完结”按钮,实现GUI程序工程创设。

 

4.  Win3二主次的入口函数将是WinMain函数,而不是控制台程序的main函数。

 

5.  GUI应用程序的入口函数是WinMain,那是2个自定义的回调函数。当中APIENT奇骏Y是_stdcall的宏定义,表明WinMain函数采取的是Windows标准调用情势。

 

6.  用户创设窗口后,就要对窗口的行为承担。当Windows向程序发送音信时,它调用程序中的一个函数,那么些函数的参数精确地描述了Windows发送的音信。在先后中称那个函数为窗口函数(Window
Procedure)或音信处理函数。它是3个自定义的回调函数。

 

7.  系统为应用程序传递全数的输入到它的各种窗口,每个窗口都关系四个窗口函数,每当那几个窗口有输入时,系统调用该函数。窗口函数处理输入,然后再将控制权交给系统。

 

8.  基于窗口界面包车型客车Windows应用程序是事件驱动(event-driven)的。为了获得输入,它们并不做显示的函数调用,而是等待系统传递输入给它们。

 

9.  在桌面上彰显四个窗口的具体步骤(也便是主程序的协会流程):

        1)注册窗口类(RegisterClassEx);

        2)创设窗口(CreateWindowEx);

        三)在桌面展现窗口(ShowWindow);

        四)更新窗口客户区(UpdateWindow);

        5)进入Infiniti的新闻获得和处理的轮回。  首先是得到音讯(GetMessage).倘使有消息到达,则将新闻分派到回到函数实行处理(DispatchMessage)。假使音讯是WM_QUIT,则GetMessage函数重回FALSE,整个新闻循环停止。音信具体的处理进程是在MainWndProc函数中展开的。

 

 

代码解释:

FirstWindow

  PS:那是3个最简便的窗口程序源代码,功用是弹出2个出色的Windows窗口。这么些代码可看做未来用API写Windows程序的主导框架。

 1 // 窗口学习01.cpp : Defines the entry point for the application.
 2 //
 3 
 4 #include "stdafx.h"
 5 LRESULT CALLBACK MainWndProc(HWND,UINT,WPARAM,LPARAM);
 6 int APIENTRY WinMain(HINSTANCE hInstance,
 7                      HINSTANCE hPrevInstance,
 8                      LPSTR     lpCmdLine,
 9                      int       nCmdShow)
10 {
11      // TODO: Place code here.
12     char szClassName[]="MainWClass";
13     WNDCLASSEX wndclass;
14     //用于描述主窗口的参数填充WNDCLASSEX结构
15     wndclass.cbSize=sizeof(wndclass);                    //结构的大小
16     wndclass.style=CS_HREDRAW|CS_VREDRAW;                //指定如果大小改变就重画
17     wndclass.lpfnWndProc=MainWndProc;                    //窗口函数指针
18     wndclass.cbClsExtra=0;                                //没有额外的类内存
19     wndclass.cbWndExtra=0;                                //没有额外的窗口内存;
20     wndclass.hInstance=hInstance;                        //实例句柄
21     wndclass.hIcon=::LoadIcon(NULL,IDI_APPLICATION);    //使用预定义图标
22     wndclass.hCursor=::LoadCursor(NULL,IDC_ARROW);        //使用预定义的光标
23     wndclass.hbrBackground=(HBRUSH)::GetStockObject(WHITE_BRUSH);    //使用白色背景画刷
24     wndclass.lpszMenuName=NULL;                            //不指定菜单
25     wndclass.lpszClassName=szClassName;                    //窗口类的名称
26     wndclass.hIconSm=NULL;                                //没有类的小图标
27     
28     //注册这个窗口类
29     ::RegisterClassEx(&wndclass);
30     
31     //创建主窗口
32     HWND hwnd=::CreateWindowEx(
33         0,                                              //dwExStyle,扩展样式
34         szClassName,                                    //lpClassName,类名
35         "My first Window!",                             //lpWindowName,标题
36         WS_OVERLAPPEDWINDOW,                            //dwStyle,窗口风格
37         CW_USEDEFAULT,                                  //X,初始X坐标
38         CW_USEDEFAULT,                                  //Y,初始Y坐标
39         CW_USEDEFAULT,                                  //nWidth,宽度
40         CW_USEDEFAULT,                                  //nHeight,高度
41         NULL,                                           //hWndParent,父窗口句柄
42         NULL,                                           //hMenu,菜单句柄
43         hInstance,                                      //hInstance,程序实例句柄
44         NULL);                                          //lpParam,用户数据
45         
46     if(hwnd==NULL)
47     {
48         ::MessageBox(NULL,"创建窗口出错!","error",MB_OK);
49         return -1;
50     }
51 
52     //显示窗口,刷新窗口客户区
53     ::ShowWindow(hwnd,nCmdShow);
54     ::UpdateWindow(hwnd);
55 
56     //从消息队列中取出消息,交给窗口函数处理,知道GetMessage返回FALSE,结束消息循环
57     MSG msg;
58     while(::GetMessage(&msg,NULL,0,0))
59     {
60         //转化键盘消息
61         ::TranslateMessage(&msg);
62         //将消息发送到相应的窗口函数
63         ::DispatchMessage(&msg);
64     }
65 
66     //当GetMessage返回FALSE时程序结束
67     return msg.wParam;
68 }
69 
70 LRESULT CALLBACK MainWndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)
71 {
72     char szText[]="最简单的窗口程序!";
73     switch(message)
74     {
75     case WM_PAINT:                //窗口客户区需要重画
76         {
77             HDC hdc;
78             PAINTSTRUCT ps;
79 
80             //使无效的客户区变得有效,并取得设备环境句柄
81             hdc=::BeginPaint(hwnd,&ps);
82             //显示文字
83             ::TextOut(hdc,10,10,szText,strlen(szText));
84             ::EndPaint(hwnd,&ps);
85             return 0;
86         }
87     case WM_DESTROY:            //正在销毁窗口
88 
89         //向消息队列投递一个WM_QUIT消息,促使GetMessage函数返回0,结束消息循环
90         ::PostQuitMessage(0);
91         return 0;
92     }
93 
94     //将我们不处理的消息交给系统做默认处理
95     return ::DefWindowProc(hwnd,message,wParam,lParam);
96 }

 

 

 程序分析:

1.  报了名窗口类:

  注册窗口类的API函数是RegisterClassEx,最终的“Ex”是扩张的情致,因为它是Win16的RegisterClass函数的进展。3个窗口类定义了窗口的有的至关心拥戴要品质,如:图标、光标、背景象和肩负处理音信的窗口函数等。这些属性定义在WNDCLASSEX结构中。

 1 typedef struct_WNDCLASSEX{
 2     UINT cbSize;                        //WNDCLASSEX结构的大小
 3     UINT style;                         //从这个窗口类派生的窗口具有的风格
 4     WNDPROC lpfnWndProc;                //即 window procedure,窗口消息处理函数指针
 5     int cbClsExtra;                     //指定紧跟在窗口类结构后的附加字节数
 6     int cbWndExtra;                     //指定紧跟在窗口事例后的附加字节数
 7     HANDLE hInstance;                   //本模板的实例句柄
 8     HICON hIcon;                        //窗口左上角图标的句柄
 9     HCURSOR hCursor;                    //光标的句柄
10     HBRUSH hbrBackground;               //背景画刷的句柄
11     LPCTSTR lpszMenuName;               //菜单名
12     LPCTSTR lpszClassName;              //该窗口类的名称
13     HICON hIconSm;                      //小图标句柄
14 }WNDCLASSEX;

  PS:

  RegisterClassEx的绝无仅有参数是其1协会的地址。注册窗口类后就足以将类名和其窗口函数、类的作风及别的的类属性联系起来。当进度在CreateWindowEx函数中钦定二个类名的时候,系统就用那个窗口函数、风格和与此类名相关的其他属性创造窗口。

  主程序中CS_HREDRAW|CS_VREDRAW风格钦命假如窗口客户区的宽度或可观改变了,则重画那个窗口。前缀CS_意为class
style,在WINUSEHummerH二.H中定义了上上下下可选样式。

  主程序中LoadIcon函数装载了贰个预约义的图标(命名叫IDI_应用程式LICATION),LoadCursor函数装载了三个预约义的光标(命名称为IDC_A奔驰M级ROW)。假诺要装载自定义的图标或光标的话,应该先向工程中添加2个财富脚本(前边打字程序将会提到),然后再通过菜单命令“Insert/Resource…”添加这几个能源。

  WHITE_BRUSH是3个Windows预订义的画刷对象类型,GetStockObject函数取得这一个画刷对象的句柄,传递给hbrBackground成员。大家也得以本身成立二个画刷对象,以便钦命喜欢的水彩作为窗口的背景象。例如下边代码将窗口的背景观设为墨土红。

wndclass.hbrBackground=::CreateSolidBrush(RGB(0xa6,0xca,0xf0));            //创建一个纯色的刷子
......
::DeleteObject(wndclass.hbrBackground);                                    //最后别忘了删除创建的刷子,释放资源

  填充完WNDCLASSEX结构,就足以开始展览注册了。RegisterClassEx函数调用退步将赶回0.

 

二.  创办窗口:

  要开创窗口,用注册的窗口类的类名调用CreateWindowEx函数即可。

 1 HWND hwnd=::CreateWindowEx(
 2                 0,                                        //dwExStyle,拓展样式
 3                 szClassName,                              //lpClassName,类名
 4                 "My first Window!",                       //lpWindowName,标题
 5                 WS_OVERLAPPEDWINDOW,                      //dwStyle,窗口风格
 6                 CW_USEDEFAULT,                            //X,初始X坐标
 7                 CW_USEDEFAULT,                            //Y,初始Y坐标
 8                 CW_USEDEFAULT,                            //nWidth,宽度
 9                 CW_USEDEFAULT,                            //nHeight,高度
10                 NULL,                                     //hWndParent,父窗口句柄
11                 NULL,                                     //hMenu,菜单句柄
12                 hInstance,                                //hInstance,程序实例句柄
13                 NULL);                                    //lpParam,用户数据
14        

  PS:

  函数调用成功将回来窗口句柄,失败重返NULL。第多个参数dwStyle的值是WS_OVE福睿斯L应用软件EDWINDOW,即重叠式窗口(Overlapped
Window)。由它内定的窗口有标题栏、系统菜单、能够更改大小的边框,以及最大化、最小化和关闭按钮。那个三个专业窗口的体制。

  上边列出有个别大面积风格的概念,它们是以WS(Windows
Style的缩写)为前缀的预订义的值。

    • WS_BOHighlanderDE哈弗  成立三个单边框的窗口
    • WS_CAPTION    创立叁个有标题框的窗口(包含WS_BODER风格)
    • WS_CHIlD  成立1个子窗口。那几个风格无法与WS_POPVP风格合用
    • WS_DISABLED  创设三个方始状态为禁止的子窗口。二个禁止状态的窗口不能够经受来自用户的输入新闻
    • WS_DLGFRAME  创设贰个带对话框边框风格的窗口。这种作风的窗口无法带标题条
    • WS_HSCROLL  创设1个有档次滚动条的窗口
    • WS_VSCROLL  成立2个有垂直滚动条的窗口
    • WS_ICONIC  创制3个始发状态为最小化状态的窗口。与WS_MINIMIZE风格一模壹样
    • WS_MAXIMIZE  创制贰个有着最大化按钮的窗口。该风格无法喝WS_EX_CONTEXTHELP风格同时出现,同时务必钦点WS_SYSMENU风格。
    • WS_THICKFRAME  成立二个可调边框的窗口,与WS_SIZEBOX风格1模一样
    • WS_VISIBLE  创造1个发端状态为可知的窗口

     等等。

 

三.  在桌面彰显窗口:

1 ::ShowWindow(hwnd,nCmdShow);

  PS:

  ShowWindow函数用于安装钦命窗口的展现状态,上边代码中的nCmdShow是系统传递给WinMain函数的参数。函数的第3个参数能够有几个不等的取值(具体景况请索引查看SDK文书档案),例如下边包车型地铁代码将隐形句柄hWnd钦命的窗口。

::ShowWindow(hWnd,SW_HIDE);            //nCmdShow参数的取值可以是SW_SHOW、SW_HIDE、SW_MINIMIZE等

 

 四.  立异窗口客户区:

1 ::UpdateWindow(hwnd);

  PS:

  若是钦点窗口的翻新区域不为空的话,UpdateWindow函数通过向那些窗口发送二个WM_PAINT新闻更新它的客户区。当窗口显示在显示器上时,窗口的客户区被在WNDCLASSEX中钦点的刷子擦去了,调用UpdateWindow函数将促使客户区重画,以展现其剧情。

 

伍.  跻身无限的音讯循环:

  程序上面将跻身Infiniti的轮回中。在调用UpdateWindow函数之后,整个窗口已经显得在桌面上,程序必须准备从用户接受键盘和鼠标输入了。Windows为各类线程维护了1个音信队列,每当有二个输入发生,Windows就把用户的输入翻译成音讯放在消息队列中。利用GetMessage函数能够从调用线程的音讯队列中取出八个音信来填充MSG结构。

1 ::GetMessage(&msg,NULL,0,0);

  PS:

  若是消息队列中并未有音讯(即未有用户输入),那些函数会一直等待下去,知道有新闻进入到新闻队列截止。msg是叁个MSG结构类型的变量,那个协会定义了新闻的全部属性。

typedef struct tagMSG {
    HWND hwnd;                   //消息要发送的窗口句柄
    UINT message;                //消息标识符,以WM_开头的预定义值(意为Window Message)
    WPARAM wParam;               //消息的参数之一
    LPARAM lParam;               //消息的参数之二
    DWORD time                   //消息放入消息队列的时间
    POINT pt;                    //这是一个POINT数据结构,表示消息放入消息队列时的鼠标位置
}MSG,*PMSG;

  GetMessage函数从新闻队列中赢得的消息壹经不是WM_QUIT,则赶回非零值。二个WM_QUIT新闻会促使GetMessage函数再次来到0,从而结束新闻循环。

::TranslateMessage(&msg);

  此调用把键盘输入翻译成为可传递的音信。

::DispatchMessage(&msg);

  DispatchMessage函数分发三个音信到相应窗口的窗口函数。在上头的例子中,窗口函数是MainWndProc。MainWndProc处理音讯后把控制权交给Windows,此时DispatchMessage函数还是一连做事,当它回到时,消息循环队列从调用Getmessage函数开首进入下1轮循环。

 

 *陆.  甩卖音信:

  新闻处理函数接收到的享有消息都被标识为2个数字,那正是MainWndProc的首先个参数uMsg。那一个数字在WINUSESportage.H文件中都以以WM_为前缀定义的。

  平常Windows程序设计者用3个switch和case结构来控制信息处理函数收到了怎么新闻,以及哪些处理这么些新闻。全数音讯处理函数不处理的新闻都必须传给二个名字为DefWindowProc的函数让Windows做私下认可处理,从DefWindowProc函数重返的值也不可能不从新闻处理函数重回。

 1 switch(uMsg)
 2 {
 3 case WM_PAINT:
 4     /*处理WM_PAINT消息*/
 5     return 0;
 6 
 7 case WM_DESTROY:
 8     /*处理WM_DESTROY消息*/
 9     return 0;
10 
11 }
12 return ::DefWindowProc(hwnd,message,wParam,lParam);

  PS:

  必需求把富有不处理的音讯交给DefWindowProc函数处理,也要把它的再次来到值重返给Windows,不然Windows就错过了与应用程序通讯的路线,也就无法再决定窗口的行为了,这是违规的。

  WM_PAINT新闻通知应用程序窗口客户区有一块只怕全部变成无效,必须刷新。那象征窗口客户区的始末必须被重画。客户区在偏下景况会成为无效:

    一)当窗口第3回被创制时,整个客户区是不行的,因为还不曾向下边画其余东西。

      第一个WM_PAINT音信被发送到窗口处理函数时,程序有机遇向客户区画壹些事物。

    二)当改变窗口大小的时候,客户区编制程序无效。

      用户在填写WNDCLASSEX结构的style成员时,将它设置为CS_HREDRAW和CS_VREDRAW,这就直接促成在改变窗口大小时Windows将整个窗口变为无效。

    三)最小化窗口,再将它过来到以前大小时,Windows未有保留整个客户区的内容。

      在图纸操作系统下,必要保留的数据太多了。同样地,Windows使这些窗口无效,窗口处理函数就会吸纳1个WM_PAINT新闻,本身负责回复客户区内容。

    四)当围着显示器移动窗口,直到窗口被掩盖时,Windows并不曾保存被覆盖的区域。

      那一个区域再度展现时,它就被标识为无用。窗口处理函数会收到多少个WM_PAINT音讯来重画窗口的始末。

  处理WM_PAINT信息时总是以调用BeginPaint函数开始。

hdc=::BeginPaint(hwnd,&ps);
//以一个EndPaint函数调用结束
::EndPaint(hwnd,&ps);

  那七个函数中,第一个参数是窗口句柄,第一个参数是指向PAINTSTRUCT结构的指正,这几个结构包罗部分得以在重画客户区时使用的音信。

  在调用BeginPaint函数的时候,假若客户区的背景还未有被擦掉的话,Windows将擦除它,擦除背景时行使的刷子由WNDCLASSEX结构的hbrBackground成员钦定。对BeginPain函数的调用将使全部客户区有效,然后回到设备条件句柄。在窗口的客户区展现图形和文字时,须求利用那个装置条件句柄。EndPaint函数负责释放设备条件句柄,使它变得不在能应用。DefWindowProc函数会通过调用BeginPaint和EndPaint函数使客户区有效。

  调用BeginPaint函数后,主程序中调用了TextOut函数。

1 ::TextOut(hdc,10,10,szText,strlen(szText));

  此函数用于在hdc钦赐的配备(那里是
显示器)上呈现文字。(十,十)为坐标地方,szText为要出示的文字,strlen(szText)语句总结出了文件占用的字节数。

  每当客户区变成无效,消息处理函数WndProc都会收到贰个洗呢WM_PAINT音讯。响应此消息的代码取得设备条件句柄后,再3回将szText的剧情展现在钦命地方。

  WM_DESTROY是窗口函数必须处理的音讯。当用户关闭窗口,而且此窗口已经响应了用户的伸手正在关闭时,音讯处理函数就会收取三个WM_DESTROY信息。当接受到这几个新闻的时候,表明窗口正在销毁。MainWndProc函数调用PostQuitMessage函数来响应此新闻。

1 ::PostQuitMessage(0);

  那一个函数向程序的音讯队列中插入一个WM_QUIT音讯。GetMessage函数假设从音信队列中获取的新闻是WM_QUIT,它将再次回到0,从而促使WinMain函数离开音信循环,然后应用程序执行以下代码。

1 //当GetMessage返回0时程序结束
2 return msg.wParam;

  此时,msg.wParam的值是传给PostQuitMessage函数的参数的值。return语句将使WinMain函数重回,程序运转结束。

相关文章