第3章窗口与消息

3.1 Windows_编程模型
 (1)窗口程序的运行过程
   ①设计窗口
   ②注册窗口类(RegisterClassEx)。在注册之前,要先填写RegisterClassEx的参数WNDCLASSEX结构。
   ③建立窗口(CreateWindowEx)。
   ④显示窗口(ShowWindows)。
   ⑤刷新窗口客户区(UpdateWindow)。
   ⑥进入无限的消息获取和处理的循环。首先获取消息(GetMessage),如果有消息到达,则将消息分派到回调函数处理(DispatchMessage),如果消息是WM_QUIT,则退出循环。

(2)窗口消息

1、进队消息
    由Windows放入消息队列中的。在程序的消息循环中,重新返回并给配给窗体过程。(如键盘、鼠标、PostMessage等)。不进队的消息在Windows调用窗口时直接发给窗口过程(如SendMessage)。也就是说,进队的消息被直接发给应用程序的消息队列。而不进队的消息发给窗口过程。在多数情况下,进队消息是用户输入的结果,比如键盘击键、鼠标移动或单击等消息。进队消息包括时钟消息、刷新消息和退出消息等。

2、不进队消息
    在多数情况下,不进队消息来自调用特定的Windows函数。例如CreateWindowEx函数会内部发送一个WM_CREATE消息、GetWindowText内部也会发送WM_GETTEXT消息获取标题内容。

(3)Window窗口和消息详细注解

 #include <Windows.h>
#include<tchar.h> /*********************************回调函数******************************************
LRESULT:返回值,被宏定义为long型
CALLBACK:函数参数进栈顺序(从右到左,己反汇编证实)
4个参数:为MSG结构体的前4个成员变量
***********************************************************************************/
LRESULT CALLBACK WndProc(HWND, int, WPARAM, LPARAM); /*************************程序入口 ******************************************
int:返回值类型;
WINAPI: 函数调用约定,进栈的顺序与CALLBACK一致
hInstance: 应用程序(进程)句柄,与指针、引用差不多,但又不同。(句柄无类型,其值会因操作系统中内存页面转换而不同,
由操作 系统维护)。
hPrevInstance:通过此参数可查看是否另一个实例进程正在运行。现在一般不用,为NULL
lpCmdLine: 命令行
nCmdShow: 窗体显示方式—正常、最大化、最小化、全屏等
***************************************************************************/
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
static TCHAR szAppName[] = TEXT("Hello Windows!"); //定义一个字符串数组,以\0结尾,用来保存程序名称
//要注册到操作系统的名称,一般为进程名称! //***********************************1、设计窗口类*****************************
WNDCLASS wc; //WNDCLASS窗体结构,wc为结构体变量
wc.style = CS_HREDRAW | CS_VREDRAW; //水平、垂直重绘,即改变窗口大小时自动发送WM_PAINT消息,而不必手动发送
wc.lpfnWndProc = (WNDPROC)WndProc; //填入回调函数的首地址
wc.cbClsExtra = ; //预留空间的附加值,此程序没用到
wc.cbWndExtra = ; //预留空间的附加值,此程序没用到
wc.hInstance = hInstance; //填入应用程序的句柄hInstance
wc.hIcon = LoadIcon(NULL, IDI_WINLOGO); //装载标题栏图标
wc.hCursor = LoadCursor(NULL, IDC_CROSS); //装载鼠标
wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); //初始化窗体背景(白色),
//从备用的库存从获取对象(句柄)
wc.lpszMenuName = NULL; //装载菜单,此处程序没有
wc.lpszClassName = szAppName;//程序的名字 //********************************2、注册窗口类*****************************
if (!RegisterClass(&wc))
{
MessageBox(NULL, TEXT("应用程序须运行于32位操作系统上!"),\
szAppName, MB_ICONERROR | MB_OK);
return ;
} //*****************************3、创建窗口类************************************
HWND hwnd;
hwnd = CreateWindow(
szAppName, //应用程序在操作系统注册的名称,
//通过这里知道是wc设计好的窗口。
TEXT("我的第一个窗口程序!"),//窗体标题名称
WS_OVERLAPPEDWINDOW, //窗体风格
CW_USEDEFAULT, //窗体左上角x坐标
CW_USEDEFAULT, //窗体左上角y坐标
CW_USEDEFAULT, //窗体右下角x坐标
CW_USEDEFAULT, //窗体右下角y坐标
NULL, //父窗口句柄,此处NULL
NULL, //菜单句柄,此处NULL
hInstance,//应用程序句柄
NULL
);
/*创建窗口完成(只存在于内存),系统会发送第一条消息WM_CREATE,注意此时,程序并未执行到消息循环处,
操作系统绕过应用程序的消息队列,直接向应用程序发出。该消息在所有的消息之前,目的是为了在程序程序执行之
前可以有机会进行一些初始化工作或装载动态链接库等操作。
*/ //**********************************4、显示窗口和更新窗口************************
ShowWindow(hwnd, nCmdShow);
/*1、单纯一个ShowWindow,照样会正确画出窗口内容,只不过是在消息队列取空之后才画的,
有时 我们希望窗口被立即重画,而不是去等待那个不确定的消息队列,此时就需要
用到UpdateWindow。UpdateWindow的作用只有一个:假若当前被标记为重画的区域存在(不
存在的话它什么也不做), 那么立刻让Windows使用SendMessage的方式来对你的窗口
发送WM_PAINT。
2、ShowWindow本身不发送重绘消息,它的作用仅仅是把窗口显示出来。不过,当窗口显示的时
候,Windows会自动探测窗口的内容是否需要重画、以及需要重画的区域
3、ShowWindow调用中会发送WM_SIZE和WM_SHOWWINDOW消息给窗体过程
*/ UpdateWindow(hwnd);
/*执行UpdateWindow时会立即发送一条WM_PAINT消息,同样由操作系统直接发出。UpdateWindow执行
时,先判断无效区域是否为空,不为空,则发送,为空为不发送。可见,将上面的ShowWindow注释掉,则
不发送。因为未显示出来,当然没有无效区域,即无效区域为空。
*/
/*UpdateWindow和I​n​v​a​l​i​d​a​t​e​的区别:
1、UpdateWindow()的作用是使窗口立即重绘(因为操作系统绕应用程序的消息队列,直接给就目标窗口发WM_PAINT,从而导致窗口立即重绘。
而Invalidate等函数后窗口不会立即重绘,这是由于WM_PAINT消息的优先级很低,它需要等消息队列中的其它消息发送完后才能被处理。
2、UpdateWindow执行时,先判断无效区域是否为空,不为空才发送消息,Invalidate的无效区为整个客户区(注意与InvalidRect的区别)
*/ //**********************消息循环*****************************
MSG msg;
/*1、调用GetMessage函数时,传入msg的地址,从应用程序的消息对列中获取消息,其中的
消息结构的内容由操作系统自动填充。
2、这是windows程序的核心,消息循环处理过程,只有在收到WM_QUIT里才退出,结束程
序。
*/
while (GetMessage(&msg, NULL, , ))
{
TranslateMessage(&msg); //翻译键盘消息
DispatchMessage(&msg); //发送消息函数,先把msg发送给操作系统,然后由操作系统再调用Wndproc函数!
//Dispatch函数的内部实现大体流程
//push 参数进栈
//............
//call WndProc(...) //调用窗体回调函数,注意这里是个回调函数。
//................
//结束,返回
} return msg.wParam;
} //*****************************回调函数——消息处理过程*******************************
//窗口回调函数,此函数只有声明和定义,没有调用!这说明此函数确实是由操作系统调用的!
LRESULT CALLBACK WndProc(HWND hWnd, int uMsg, WPARAM wParam, LPARAM lParam)
{
HDC hDC; //设备环境——绘图的地方
PAINTSTRUCT ps; //绘图结构体变量
RECT rect; //绘图区范围
switch (uMsg) //分别处理不同的消息
{
case WM_CREATE://此消息是一个应用程序发送的第一个消息,也是唯一的一次!
PlaySound(TEXT("HelloWin.wav"), NULL, SND_FILENAME | SND_SYNC);
break;
case WM_SIZE:
//GetClientRect(hWnd, &rect);
//InvalidateRect(hWnd, &rect, TRUE);
break;
case WM_CHAR:
TCHAR szChar[];
_stprintf_s(szChar, TEXT("您按下了%c"), wParam);
MessageBox(hWnd, szChar, TEXT("地平线工作室!"), );
break;
case WM_PAINT://绘图函数,在窗口上画画儿!
//PlaySound(TEXT("HelloWin.wav"), NULL, SND_FILENAME | SND_SYNC); hDC = BeginPaint(hWnd, &ps);
/*ps结构的第3个成员为RECT rcPaint,是指一个无效区域当调用BeginPaint函数时,由操
作系 统填充。因此,严格来讲,hDC为无效区效的“设备环境”,在此区域外的作图,都
将被忽略(即xy坐标在该区域外的)。 */ GetClientRect(hWnd, &rect); //获得客户区大小
TextOut(hDC, , , TEXT("地平线工作室!"), lstrlen(TEXT("地平线工作室!")));
DrawText(hDC, TEXT("我的第一个窗口程序!"), -, &rect,DT_CENTER | DT_VCENTER | DT_SINGLELINE);
Ellipse(hDC, , , , );
EndPaint(hWnd, &ps);
break;
case WM_LBUTTONDOWN:
MessageBox(hWnd,TEXT("单击左键!"), TEXT("地平线工作室!"), );
break;
case WM_CLOSE:
if (IDYES == MessageBox(hWnd,TEXT("是否真的要退出!"),TEXT("温馨提醒!"),MB_YESNO))
{
DestroyWindow(hWnd);
}
break;
case WM_DESTROY://处理退出消息
PostQuitMessage();//此消息直接进入消息队列的头部!
break;
default:
return DefWindowProc(hWnd,uMsg,wParam,lParam);
}
return ;
}
/**********************************************注意*******************************************
1:所有的消息都由操作系统负责管理,所有的消息先进入总的系统消息队列,再由系统消息队列向各个小应用程序队列发消息!
2:窗口回调函数Wndproc是由操作系统调用的,所有的消息进入消息循环,由消息循环把消息发到操作系统的队列中,在由操作系统
根据消息来调用Wndproc函数,而Wndproc函数根据message的不同而调用相应的处理过程!
3:上面这个程序你可以改造一下,在添加几个消息处理过程,比如鼠标左键按下WM_LBULLONDOWN,鼠标左键抬起WM_LBULLONUP
定时器消息WM_TIMER,绘图消息WM_PAINT消息等等,然后把PlaySound (TEXT ("hellowin.wav"), NULL, SND_FILENAME | SND_ASYNC)
这个函数添加到以上各个消息处理过程中,看看结果是不是进行了相应的操作!比如,点击一下鼠标左键看看是不是发出声音了!
可以自己试试!
4:其实,WINDOWS程序也可以自己去创建函数!自己去自定义消息处理过程!比如,你自己定义了一个函数,想在鼠标左键点击后
去调用这个函数,那就可以在WM_LBULLONDOWN这个消息处理过程中加上你自己定义的函数调用就可以了!
5:上面这个程序是个非常简单的程序,但是他足以说明WINDOWS程序的消息机制!几乎所有的程序都包括上面这段代码(除了PLAYSOUND函数和DrawText函数),不管多么复杂的WINDOWS程序都必须包括上面这段代码,因为他是再简单不过的了,只是创建了一个窗口!但是,再复杂的程序无非也就是在Wndproc函数中的switch多加几个case 吗!无非就是加上什么鼠标,键盘,定时器等等,但原里都是一样的!他的消息处理过程都是一样的!主要是把他的消息机制弄懂,其他的都非常easy!
6:PlaySound函数需要用到WINMM.LIB库,在链接时需要加入到项目中。方法:“解决方案”->“项目”->右键->"属性"-> "配置属性"->"链接器"->"输入"->在“附加依赖项”中填入“WINMM.LIB;”
或者:
在#include<windows.h>后面加上(注意:不能加在前面):
#include <mmsystem.h>
#pragma comment(lib, "WINMM.LIB")
******************************************************************************************/

第3章 窗口与消息_3.1Windows编程模型的更多相关文章

  1. Windows程序设计(第五版)学习:第三章 窗口与消息

        第三章 窗口与消息 1,windows窗口过程:应用程序所创建的每一个窗口都有一个与之关联的窗口过程,用于处理传递给窗口的消息. 2,窗口依据窗口类来创建.窗口类标识了用于处理传递给窗口的消息 ...

  2. 2D游戏编程1--windows编程模型

    一.创建一个windows程序步骤 1.创建一个windows类 2.创建一个事件处理程序 3.注册windows类 4.用之前创建的windows类创建一个窗口 5.创建一个主事件循环   二.存储 ...

  3. C#代码像QQ的右下角消息框一样,无论现在用户的焦点在哪个窗口,消息框弹出后都不影响焦点的变化,那么有两种方法

    你QQ的右下角消息框一样,无论现在用户的焦点在哪个窗口,消息框弹出后都不影响焦点的变化,那么有两种方法: 要么重写需要弹出的窗体的事件: protected override CreateParams ...

  4. DuiLib(一)——窗口及消息

    最近看了下开源界面库duilib的代码,写几篇相关的文章.网上已经有好多相关的文章了,我这里只是记录自己的学习过程,写到哪里算哪里,权当自娱自乐. duilib是一轻量级的direcui界面库,所谓d ...

  5. 《Android开发艺术探索》读书笔记 (10) 第10章 Android的消息机制

    第10章 Android的消息机制 10.1 Android消息机制概述 (1)Android的消息机制主要是指Handler的运行机制,其底层需要MessageQueue和Looper的支撑.Mes ...

  6. 眼见为实(2):介绍Windows的窗口、消息、子类化和超类化

    眼见为实(2):介绍Windows的窗口.消息.子类化和超类化 这篇文章本来只是想介绍一下子类化和超类化这两个比较“生僻”的名词.为了叙述的完整性而讨论了Windows的窗口和消息,也简要讨论了进程和 ...

  7. 窗口、消息查看分析利器Spy++

    Spy++ —— 窗口.消息查看分析利器 Spy++ —— 窗口.消息查看分析利器 2016年07月15日 00:25:22 阅读数:23170 1,简介   Microsoft Spy++是一个非常 ...

  8. Windows核心编程 第26章 窗口消 息

    窗 口 消 息 Wi n d o w s允许一个进程至多建立10 000个不同类型的用户对象(User object):图符.光标.窗口类.菜单.加速键表等等.当一个线程调用一个函数来建立某个对象时, ...

  9. 【黑金原创教程】【TimeQuest】【第五章】网表质量与外部模型

    声明:本文为黑金动力社区(http://www.heijin.org)原创教程,如需转载请注明出处,谢谢! 黑金动力社区2013年原创教程连载计划: http://www.cnblogs.com/al ...

随机推荐

  1. Activity的四种启动模式

    Activity有四种启动模式: 1. standard,默认的启动模式,只要激活Activity,就会创建一个新的实例,并放入任务栈中,这样任务栈中可能同时有一个Activity的多个实例. 2. ...

  2. 《数据结构与算法Python语言描述》习题第二章第三题(python版)

    ADT Rational: #定义有理数的抽象数据类型 Rational(self, int num, int den) #构造有理数num/den +(self, Rational r2) #求出本 ...

  3. Cats(4)- 叠加Free程序运算结果,Stacking monadic result types

    在前面的几篇关于Free编程的讨论示范中我们均使用了基础类型的运算结果.但在实际应用中因为需要考虑运算中出现异常的情况,常常会需要到更高阶复杂的运算结果类型如Option.Xor等.因为Monad无法 ...

  4. Java入门第一章

    后天就是十一长假了,亲们准备好了去哪儿玩了吗? 今天有点空,就来聊聊Java吧,当然是一些Java入门知识了,网上有很多,这里我只是列举一些我自己学到的,感谢大家关注喵的博客这么久,也为大家带来点新知 ...

  5. UML(Unified Modeling Language)统一建模语言

    什么是模型 模型是对现实的简化 模型是提供系统的蓝图,模型可是包括详细计划.也可是是从更高程度考虑系统的总体计划,每个系统可以从不同的方面用不通过的模型来描述.因而每个模型都是在语义上闭合的抽象系统. ...

  6. Code First :使用Entity. Framework编程(5) ----转发 收藏

    第五章 对数据库映射使用默认规则与配置 到目前为止我们已经领略了Code First的默认规则与配置对属性.类间关系的影响.在这两个领域内,Code First不仅影响模型也影响数据库.在这一章,你将 ...

  7. 【JavaScript】浅析javaScript和HTML与unicode字符集的关系

    目录结构: // contents structure [-] javaScript和HTML的字符集 javaScript和HTML如何表现unicode字符集 参考文章 javaScript和HT ...

  8. js中Unicode转义序列

    将某一中文字符转义,可采取在线工具进行转义,http://tool.chinaz.com/tools/unicode.aspx "哈哈" ==="\u54c8\u54c8 ...

  9. gcc boost版本冲突解决日记

    问题背景 项目在Ubuntu10 64位 boost 1.55,boost采用的是项目内包含相对目录的形式部署 项目采用了 -Wall -Wextra -Werror -Wconversion 最高的 ...

  10. SharePoint 2013 User Profile Services之跨场发布

    在之前博客中已经描述了User Profile的两种配置场景,这篇博客将详细介绍微软官方推荐的配置方法. 测试环境的架构可以参考之前的博客内容,这里就不做介绍了,直接切入主题. 1. 在sp-farm ...