学习《Windows程序设计》记录

概念贴士:

1.  每个GUI应用程序至少应该创建一个窗口,称为主窗口,它作为用户与应用程序间的主界面来提供服务。大多数应用程序也直接或间接地创建其他窗口,来完成与主窗口相关的工作。

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

3.  VC++6.0创建GUI程序:  1)创建VC++6.0,选择命令“文件”,点击“新建”;

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

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

                4)点击OK按钮,选择第二项“一个简单Win32应用程序”;

                5)点击“完成”按钮,完成GUI程序工程创建。

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

5.  GUI应用程序的入口函数是WinMain,这是一个自定义的回调函数。其中APIENTRY是_stdcall的宏定义,说明WinMain函数采用的是Windows标准调用方式。

6.  用户创建窗口后,就要对窗口的行为负责。当Windows向程序发送消息时,它调用程序中的一个函数,这个函数的参数精确地描述了Windows发送的消息。在程序中称这个函数为窗口函数(Window Procedure)或消息处理函数。它是一个自定义的回调函数。

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

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

9.  在桌面上显示一个窗口的具体步骤(也就是主程序的结构流程):

        1)注册窗口类(RegisterClassEx);

        2)创建窗口(CreateWindowEx);

        3)在桌面显示窗口(ShowWindow);

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

        5)进入无限的消息获取和处理的循环。  首先是获取消息(GetMessage).如果有消息到达,则将消息分派到回到函数进行处理(DispatchMessage)。如果消息是WM_QUIT,则GetMessage函数返回FALSE,整个消息循环结束。消息具体的处理过程是在MainWndProc函数中进行的。

代码解释:

FirstWindow

  PS:这是一个最简单的窗口程序源代码,作用是弹出一个典型的Windows窗口。这些代码可作为今后用API写Windows程序的基本框架。

 // 窗口学习01.cpp : Defines the entry point for the application.
// #include "stdafx.h"
LRESULT CALLBACK MainWndProc(HWND,UINT,WPARAM,LPARAM);
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
// TODO: Place code here.
char szClassName[]="MainWClass";
WNDCLASSEX wndclass;
//用于描述主窗口的参数填充WNDCLASSEX结构
wndclass.cbSize=sizeof(wndclass); //结构的大小
wndclass.style=CS_HREDRAW|CS_VREDRAW; //指定如果大小改变就重画
wndclass.lpfnWndProc=MainWndProc; //窗口函数指针
wndclass.cbClsExtra=; //没有额外的类内存
wndclass.cbWndExtra=; //没有额外的窗口内存;
wndclass.hInstance=hInstance; //实例句柄
wndclass.hIcon=::LoadIcon(NULL,IDI_APPLICATION); //使用预定义图标
wndclass.hCursor=::LoadCursor(NULL,IDC_ARROW); //使用预定义的光标
wndclass.hbrBackground=(HBRUSH)::GetStockObject(WHITE_BRUSH); //使用白色背景画刷
wndclass.lpszMenuName=NULL; //不指定菜单
wndclass.lpszClassName=szClassName; //窗口类的名称
wndclass.hIconSm=NULL; //没有类的小图标 //注册这个窗口类
::RegisterClassEx(&wndclass); //创建主窗口
HWND hwnd=::CreateWindowEx(
, //dwExStyle,扩展样式
szClassName, //lpClassName,类名
"My first Window!", //lpWindowName,标题
WS_OVERLAPPEDWINDOW, //dwStyle,窗口风格
CW_USEDEFAULT, //X,初始X坐标
CW_USEDEFAULT, //Y,初始Y坐标
CW_USEDEFAULT, //nWidth,宽度
CW_USEDEFAULT, //nHeight,高度
NULL, //hWndParent,父窗口句柄
NULL, //hMenu,菜单句柄
hInstance, //hInstance,程序实例句柄
NULL); //lpParam,用户数据 if(hwnd==NULL)
{
::MessageBox(NULL,"创建窗口出错!","error",MB_OK);
return -;
} //显示窗口,刷新窗口客户区
::ShowWindow(hwnd,nCmdShow);
::UpdateWindow(hwnd); //从消息队列中取出消息,交给窗口函数处理,知道GetMessage返回FALSE,结束消息循环
MSG msg;
while(::GetMessage(&msg,NULL,,))
{
//转化键盘消息
::TranslateMessage(&msg);
//将消息发送到相应的窗口函数
::DispatchMessage(&msg);
} //当GetMessage返回FALSE时程序结束
return msg.wParam;
} LRESULT CALLBACK MainWndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)
{
char szText[]="最简单的窗口程序!";
switch(message)
{
case WM_PAINT: //窗口客户区需要重画
{
HDC hdc;
PAINTSTRUCT ps; //使无效的客户区变得有效,并取得设备环境句柄
hdc=::BeginPaint(hwnd,&ps);
//显示文字
::TextOut(hdc,,,szText,strlen(szText));
::EndPaint(hwnd,&ps);
return ;
}
case WM_DESTROY: //正在销毁窗口 //向消息队列投递一个WM_QUIT消息,促使GetMessage函数返回0,结束消息循环
::PostQuitMessage();
return ;
} //将我们不处理的消息交给系统做默认处理
return ::DefWindowProc(hwnd,message,wParam,lParam);
}

 程序分析:

1.  注册窗口类:

  注册窗口类的API函数是RegisterClassEx,最后的“Ex”是扩展的意思,因为它是Win16的RegisterClass函数的拓展。一个窗口类定义了窗口的一些主要属性,如:图标、光标、背景色和负责处理消息的窗口函数等。这些属性定义在WNDCLASSEX结构中。

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

  PS:

  RegisterClassEx的唯一参数是这个结构的地址。注册窗口类后就可以将类名和其窗口函数、类的风格及其他的类属性联系起来。当进程在CreateWindowEx函数中指定一个类名的时候,系统就用这个窗口函数、风格和与此类名相关的其他属性创建窗口。

  主程序中CS_HREDRAW|CS_VREDRAW风格指定如果窗口客户区的宽度或高度改变了,则重画这个窗口。前缀CS_意为class style,在WINUSER.H中定义了全部可选样式。

  主程序中LoadIcon函数装载了一个预定义的图标(命名为IDI_APPLICATION),LoadCursor函数装载了一个预定义的光标(命名为IDC_ARROW)。如果要装载自定义的图标或光标的话,应该先向工程中添加一个资源脚本(后面打字程序将会提到),然后再通过菜单命令“Insert/Resource...”添加这些资源。

  WHITE_BRUSH是一个Windows预定义的画刷对象类型,GetStockObject函数取得这个画刷对象的句柄,传递给hbrBackground成员。我们也可以自己创建一个画刷对象,以便指定喜欢的颜色作为窗口的背景色。例如下面代码将窗口的背景色设为天蓝色。

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

  填充完WNDCLASSEX结构,就可以进行注册了。RegisterClassEx函数调用失败将返回0.

2.  创建窗口:

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

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

  PS:

  函数调用成功将返回窗口句柄,失败返回NULL。第四个参数dwStyle的值是WS_OVERLAPPEDWINDOW,即重叠式窗口(Overlapped Window)。由它指定的窗口有标题栏、系统菜单、可以改变大小的边框,以及最大化、最小化和关闭按钮。这个一个标准窗口的样式。

  下面列出一些常见风格的定义,它们是以WS(Windows Style的缩写)为前缀的预定义的值。

    • WS_BORDER  创建一个单边框的窗口
    • WS_CAPTION    创建一个有标题框的窗口(包括WS_BODER风格)
    • WS_CHIlD  创建一个子窗口。这个风格不能与WS_POPVP风格合用
    • WS_DISABLED  创建一个初始状态为禁止的子窗口。一个禁止状态的窗口不能接受来自用户的输入信息
    • WS_DLGFRAME  创建一个带对话框边框风格的窗口。这种风格的窗口不能带标题条
    • WS_HSCROLL  创建一个有水平滚动条的窗口
    • WS_VSCROLL  创建一个有垂直滚动条的窗口
    • WS_ICONIC  创建一个初始状态为最小化状态的窗口。与WS_MINIMIZE风格相同
    • WS_MAXIMIZE  创建一个具有最大化按钮的窗口。该风格不能喝WS_EX_CONTEXTHELP风格同时出现,同时必须指定WS_SYSMENU风格。
    • WS_THICKFRAME  创建一个可调边框的窗口,与WS_SIZEBOX风格相同
    • WS_VISIBLE  创建一个初始状态为可见的窗口

     等等。

3.  在桌面显示窗口:

 ::ShowWindow(hwnd,nCmdShow);

  PS:

  ShowWindow函数用于设置指定窗口的显示状态,上面代码中的nCmdShow是系统传递给WinMain函数的参数。函数的第二个参数可以有多个不同的取值(具体情况请索引查看SDK文档),例如下面的代码将隐藏句柄hWnd指定的窗口。

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

 4.  更新窗口客户区:

 ::UpdateWindow(hwnd);

  PS:

  如果指定窗口的更新区域不为空的话,UpdateWindow函数通过向这个窗口发送一个WM_PAINT消息更新它的客户区。当窗口显示在屏幕上时,窗口的客户区被在WNDCLASSEX中指定的刷子擦去了,调用UpdateWindow函数将促使客户区重画,以显示其内容。

5.  进入无限的消息循环:

  程序下面将进入无限的循环中。在调用UpdateWindow函数之后,整个窗口已经显示在桌面上,程序必须准备从用户接受键盘和鼠标输入了。Windows为每个线程维护了一个消息队列,每当有一个输入发生,Windows就把用户的输入翻译成消息放在消息队列中。利用GetMessage函数可以从调用线程的消息队列中取出一个消息来填充MSG结构。

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

  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函数开始进入下一轮循环。

 *6.  处理消息:

  消息处理函数接收到的所有消息都被标识为一个数字,这就是MainWndProc的第一个参数uMsg。这些数字在WINUSER.H文件中都是以WM_为前缀定义的。

  通常Windows程序设计者用一个switch和case结构来决定消息处理函数收到了什么消息,以及如何处理这个消息。所有消息处理函数不处理的消息都必须传给一个名为DefWindowProc的函数让Windows做默认处理,从DefWindowProc函数返回的值也必须从消息处理函数返回。

 switch(uMsg)
{
case WM_PAINT:
/*处理WM_PAINT消息*/
return ; case WM_DESTROY:
/*处理WM_DESTROY消息*/
return ; }
return ::DefWindowProc(hwnd,message,wParam,lParam);

  PS:

  必须要把所有不处理的消息交给DefWindowProc函数处理,也要把它的返回值返回给Windows,否则Windows就失去了与应用程序通信的途径,也就不能再控制窗口的行为了,这是不合法的。

  WM_PAINT消息通知应用程序窗口客户区有一块或者全部变成无效,必须刷新。这意味着窗口客户区的内容必须被重画。客户区在以下情况会变成无效:

    1)当窗口第一次被创建时,整个客户区是无效的,因为还没有向上面画任何东西。

      第一个WM_PAINT消息被发送到窗口处理函数时,程序有机会向客户区画一些东西。

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

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

    3)最小化窗口,再将它恢复到以前大小时,Windows没有保存整个客户区的内容。

      在图形操作系统下,需要保存的数据太多了。同样地,Windows使这个窗口无效,窗口处理函数就会收到一个WM_PAINT消息,自己负责回复客户区内容。

    4)当围着屏幕移动窗口,直到窗口被覆盖时,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函数。

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

  此函数用于在hdc指定的设备(这里是 显示器)上显示文字。(10,10)为坐标位置,szText为要显示的文字,strlen(szText)语句计算出了文本占用的字节数。

  每当客户区变成无效,消息处理函数WndProc都会收到一个洗呢WM_PAINT消息。响应此消息的代码取得设备环境句柄后,再一次将szText的内容显示在指定位置。

  WM_DESTROY是窗口函数必须处理的消息。当用户关闭窗口,而且此窗口已经响应了用户的请求正在关闭时,消息处理函数就会收到一个WM_DESTROY消息。当接受到这个消息的时候,说明窗口正在销毁。MainWndProc函数调用PostQuitMessage函数来响应此消息。

 ::PostQuitMessage();

  这个函数向程序的消息队列中插入一个WM_QUIT消息。GetMessage函数如果从消息队列中取得的消息是WM_QUIT,它将返回0,从而促使WinMain函数离开消息循环,然后应用程序执行以下代码。

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

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

第四章 Windows图形界面-上的更多相关文章

  1. C# 6 与 .NET Core 1.0 高级编程 - 39 章 Windows 服务(上)

    译文,个人原创,转载请注明出处(C# 6 与 .NET Core 1.0 高级编程 - 39 章 Windows 服务(上)),不对的地方欢迎指出与交流. 章节出自<Professional C ...

  2. python 教程 第十九章、 图形界面编程

    第十九章. 图形界面编程 import Tkinter top = Tkinter.Tk() hello = Tkinter.Label(top, text='Hello World!') hello ...

  3. C和指针 (pointers on C)——第四章:语句(上)

    第四章--语句(上) 总结总结!!! C没有布尔类型,所以在一些逻辑推断时候必须用整型表达式,零值为假,非零值为真. for比while把控制循环的表达式收集起来放在一个地方,以便寻找. do语句比w ...

  4. 第四章 Windows的图形设备接口及Windows绘图 P83 4-6

    实验(实习)名称  图形程序设计及其应用  实验(实习)日期 一.实验目的 1.熟悉图形设备接口的概念及其应用. 二.实验内容及步骤  实验任务 1.熟悉基于图形应用的程序设计: 2.掌握刷新技术及其 ...

  5. 04747_Java语言程序设计(一)_第6章_图形界面设计(二)

    例6.1声明一个面板子类,面板子类对象有3个选择框. class Panel1 extends JPanel { JCheckBox box1, box2, box3; Panel1() { box1 ...

  6. 04747_Java语言程序设计(一)_第5章_图形界面设计(一)

    例5.1一个用JFrame类创建窗口的Java应用程序. import javax.swing.*; public class Example5_1 { public static void main ...

  7. Linux认知之旅【06 图形界面上的各种折腾】!

    玩linux免不了折腾,不折腾对不起linux 初次接触, 总会接触到绚丽的linux桌面! 但是随之而来的桌面优化,字体安装,操作习惯都需要一一适应

  8. 第四章 常用API(上)

    4.1.Object类 描述:该类是所有类的最终根类 方法 描述 public boolean equals(Object obj) 表示某个其它对象是否"等于"此对象 publi ...

  9. win10创建Ubuntu16.04子系统,安装常用软件以及图形界面(包括win10远程桌面连接Ubuntu)

    一.开启win10子系统 [ Windows Subsystem for Linux(WSL)] 二.基本配置 三.安装常用的软件 安装配置zsh 使用 bash 客户端软件 cmder(其实是win ...

随机推荐

  1. F02 金融学第二定律 资金的积聚

    美国南北战争,北方取胜的关键在于发行了债券,从而积聚了资金,提升了北方军队战斗力. 纽约的逆袭,得益于伊利运河的修建,而伊利运河的建造需要的资金,全靠债券发行积聚的资金. 聚积起来的资金,往往决定了重 ...

  2. cookies和re

    参考:http://cuiqingcai.com/968.html   http://cuiqingcai.com/977.html

  3. gunicorn 信号处理(SIGHUP,SIGUSR2)

    在这篇文章中,提到了Master进程对信号的处理函数,其中有两个信号比较有意思. SIGHUP:用来热更新(Reload)应用 SIGUSR2:用来在线升级(upgrade on the fly)gu ...

  4. HTML页面中JavaScript能获取到的各种屏幕大小信息

    在HTML页面中,通过JavaScript代码访问 window 对象,能够获取到很多表征屏幕大小的信息,下面列举并加以区分. window 对象中的屏幕信息 window.innerheight. ...

  5. asp.net mvc 下拉列表

    第一步:新建一个格式化下拉列表的公共类文件 using System; using System.Collections; using System.Collections.Generic; usin ...

  6. CSS文字不换行,溢出省略

    white-space:nowrap; overflow:hidden; text-overflow:ellipsis;

  7. Swift tableview自带的刷新控件

    import UIKit class ViewController: UIViewController,UITableViewDelegate,UITableViewDataSource { let ...

  8. Scala 安装 Exception in thread "main" java.lang.VerifyError: Uninitialized object exists on backward branch 96

    windows下载安装完最新版本的Scala(2.12.4)后,终端如下错误 C:\Users\Administrator>scala -versionException in thread & ...

  9. css:background-position > 精灵技术

    background-position : length || length background-position : position || position 取值: length  : 百分数 ...

  10. adb指令介绍

    一.adb命令格式为:adb [-d|-e|-s <serialNumber>] <command> 1.adb devices :列出当前电脑所连接的所有安卓设备 2.adb ...