WINDOWS消息和窗口简介
一、WINDOWS的消息和窗口简介:
1、什么是windows在这里我就不介绍了,但是作为一个程序员我们要知道WINDOWS最重要的一个也是我们程序员常用的一个东西就是消息。窗口是以消息的形式输入的,窗口也用消息与其它窗口通讯。
2、我们常会说windows给程序发送了一个消息,其实这是指windows调用程序中的一个函数,该函数的参数描述了这个特定消息。这种位于windows程序中的函数称为“窗体消息处理程序”。程序建立的每一个窗体都有相关的窗口消息处理程序。这个窗口消息处理程序是一个函数,既可以在程序中,也可以在动态链接库中。Windows通过调用窗口消息处理程序来给窗体发送消息。窗口消息处理程序根据此消息进行处理,然后将控制传回给windows。
3、在对象导向的程序设计中,对象是程序与数据的组合。窗口是一种对象,其程序是窗口消息处理程序。数据是窗口消息处理程序保存的信息和windows为每个窗口以及系统中那个窗口类别保存的信息。
4、windows程序开始执行后,windows为该程序建立一个“消息队列”。这个消息队列用来存放该程序可能建立的各种不同窗口的消息。程序中有一小段程序代码,叫做“消息循环”,用来从队列中取出消息,并且将它们发送给相应的窗口消息处理程序。有些消息直接发送给窗口消息处理程序,不用放入消息队列中。
二、一个真正的windows程序:
// HELLOWIN.C
/*------------------------------------------------------------------------
HELLOWIN.C -- Displays "Hello, Windows 98!" in client area
(c) Charles Petzold, 1998
-----------------------------------------------------------------------*/
#include <windows.h>
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
// TEXT 宏——UNICODE标识
static TCHAR szAppName[] = TEXT ("HelloWin") ;
HWND hwnd ;
MSG msg ;
WNDCLAS wndclass ; // 窗口类 wndclass.style = CS_HREDRAW | CS_VREDRAW ; // 窗口类风格
wndclass.lpfnWndProc = WndProc ; // 窗口过程
wndclass.cbClsExtra = ; // 在类结构保存的窗口结构中预留一些额外空间
wndclass.cbWndExtra = ; // 在Windows内部保存的窗口结构中预留一些额外空间
wndclass.hInstance = hInstance ; // 程序的实例句柄
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ; // 加载图标资源
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; // 加载光标资源
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ; // GetStockObject获取(画笔、刷子等)GDI对象
wndclass.lpszMenuName = NULL ; // 指定窗口类菜单
wndclass.lpszClassName = szAppName ; // 指定窗口类名 if (!RegisterClass (&wndclass)) // 注册窗口类
{
MessageBox ( NULL, TEXT ("This program requires Windows NT!"), szAppName, MB_ICONERROR) ;
return ;
}
hwnd = CreateWindow(
szAppName, // window class name
TEXT ("The Hello Program"), // window caption
WS_OVERLAPPEDWINDOW, // window style
CW_USEDEFAULT, // initial x position
CW_USEDEFAULT, // initial y position
CW_USEDEFAULT, // initial x size
CW_USEDEFAULT, // initial y size
NULL, // 父窗口句柄
NULL, // 菜单句柄
hInstance, // program instance handle
NULL
) ; // creation parameters // 窗口创建成功后,系统将在内存中为其分配一块内存,但是此时窗口并未显示在显示器上,所以需要使用两个调用:
ShowWindow (hwnd, iCmdShow) ; // 显示窗口
UpdateWindow (hwnd) ; // 将导致客户区被绘制 // 程序通过执行一个叫做“消息循环”的代码从消息队列中取出消息
// 注意GetMessage(阻塞,若没有消息则不返还控制权)和PeekMessage(非阻塞,若没有消息也会返回)的区别
while (GetMessage (&msg, NULL, , ))
{
TranslateMessage (&msg) ; // 将msg结构传给Windows,进行一些键盘转换
DispatchMessage (&msg) ; // 将msg结构传给Windows,然后Windows将里面的消息发给相应的窗口过程进行处理
} return msg.wParam ;
} // CALLBACK类型(供系统调用)
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc ;
PAINTSTRUCT ps ;
RECT rect ;
switch (message)
{
// 当Windows在处理CreateWindow函数时,窗口过程就会接收到WM_CREATE消息
case WM_CREATE:
PlaySound (TEXT ("hellowin.wav"), NULL, SND_FILENAME | SND_ASYNC) ;
return ;
// 当窗口客户区域的一部分或全部变成“无效”时,必须进行刷新,WM_PAINT将通知程序
case WM_PAINT:
// BeginPaint调用使整个客户区有效,并返回一个“设备环境句柄
hdc = BeginPaint (hwnd, &ps) ;
// 当改变窗口大小时,WndProc通过调用GetClientRect来获取变化后的窗口大小,重新绘制客户区
GetClientRect (hwnd, &rect) ;
// 在指定的区域内输出格式化的文本
DrawText (hdc, TEXT ("Hello, Windows 98!"), -, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER) ;
EndPaint (hwnd, &ps) ;
return ;
// 当用户点击关闭按钮时发生
case WM_DESTROY:
// 调用PostQuitMessage以标准方式响应WM_DESTROY消息;
// PostQuitMessage(0); // 该函数在程序的消息队列插入一个WM_QUIT消息。
// GetMessage对于除了WM_QUIT消息之外的从消息队列中取出的所有消息都返回非0值。而当GetMessage取到一个WM_QUIT消息时,返回0
PostQuitMessage () ;
return ;
}
return DefWindowProc (hwnd, message, wParam, lParam) ;
}
1、代码分析:
(1)、在这里你除了winMain你还可以看到一个WndPro的函数这个函数就是传说中的消息处理函数,即消息循环就在这里运行的也是一个程序的心脏,有了他程序才可以得到正常的运行,他在窗口注册类的类里就被移植入窗口类中wndclass.lpfnWndProc=WndPro;要先申明后使用。
(2)、在这里我还要说一下下CreateWindow和WNDCLASS也就是建立窗口和注册窗口的联系,他们之间就好比造车厂和设计车的关系,RegisterClass函数注册了一个WNDCLASS的对象,然后CreateWindow通过其中的lpClassName这个参数也就是WNDCLASS对象的lpszClassName与窗口类联系起来,lpszClassName都是字符串的类型。
(3)、windows函数的调用:这个基本的框架里我们调用了18个windows 函数如下:
LoadIcon // 加载图标代程序使用
LoadCursor // 加载鼠标光标供程序使用
GetStockObject // 取得一个图形对象
RegisterClass // 为程序窗口注册窗口类别
MessageBox // 显示消息框
CreateWindow // 根据窗口类别建立一个窗口
ShowWindow // 在屏幕上显示窗口
UpdateWindow // 指示窗口自我更新
GetMessage // 从消息队列中取得消息
TranslateMessage // 转译某些键盘消息
DispatchMessage // 将消息发送给窗口消息处理程序
PlaySound // 播放一个声音文件
BeginPaint // 开始绘制窗口
GetClientRect // 取得窗口显示区域的大小
DrawText // 显示字符串
EndPaint // 结束绘制窗口
PostQuitMessage // 在消息队列中插入一个退出消息
DefWindowProc // 执行内定消息处理
(4)、在这个函数里面有很多大小写字母宏定义:这些解释一下下
在上面的函数中出现了如下几个大写字母宏定义:
前缀: 类别 使用
CS 窗口类别样式 WNDCLASS.style=CS_HREDRAW|CS_VREDRAW;
CW 建立窗口 CreateWindow---(CW)
DT 绘制文字 DrawText----(DT)
IDI 图示ID LoadIcon(...)
IDC 鼠标ID LoadCursor(...)
MB 消息框 MessageBox------MB
SND 声音 PlaySound (...)
WM 窗口消息 WindowsMessage-------(WM)
WS 窗口样式 WindowStyle-----(WS)
(5)、我们常会问WPARAM和LPARAM有什么区别干什么用: 其实这两个参数是win16和win32并存时留下的产物现在都是32位了,原来wParam(WORD)是一个16位的lParam(LONG)是32位的,现在的WPARAM和LPARAM都是MS重新定义的,他们通常是与一个消息有关的常量值如下: wParam 通常是一个与消息有关的常量值,也可能是窗口或控件的句柄。 lParam 通常是一个指向内存中数据的指针。 由于wParam、lParam和指针都是32位的,需要时可以强制类型转换。具体表示什么,与message相关,他们是事先定义好的。
(6)、WndPro函数传回一个形态为LRESULT的值,议该值简单地被定义为一个LONG,在这里WndPro函数被指定成一个CALLBACK形态,其实也是一个和WINAPI一样的宏定义他是一个_stdcall一个从右到左的压栈方式,也就是传说中的回调函数(纸老虎不用怕)。
2、句柄简介: 句柄是一个(通常为32位的)整数,它代表一个对象。Windows中的够本类似传统C或MS-Dos程序设计中使用的文件句柄。程序几乎总是通过呼叫window函数取得句柄。程序在函数中使用够本,用以代表对象。句柄值对程序来说是无关紧要的,但是windows对于这个代号来说他就可以利用这个代号(句柄)使用相对应的对象。 一句话句柄是什么,比如你是一个对象那么句柄就是你的名字,如果windows要叫你去买酱油那么他会给你说:“XX句柄去买酱油”,和叫对象去买酱油的效果是一样,但是用了句柄会更好管理。(我不知道这样理解是否会好一点!) 三个大写标识符,用于不同形态的句柄:
标识符 含义
HINSTANCE 执行实体(程序自身)句柄
HWND 窗口句柄
HDC 设备内容句柄
句柄用的很频繁比如:HICON(图标句柄)、HCURSOR(鼠标光标句柄)、HBRUSH(画刷句柄)、windows中的每个窗口都有一个够本,程序用够本来使用窗口窗体句柄是windows程序所处理最重要的句柄之一。
3、我顺便也说一下instance吧,其实这就相当于是你运行QQ整个QQ就是一个instance!
三、注册窗口类别:
1、窗口依照某一窗口类别建立,窗口类别用以标识处理窗口消息的消息处理程序。窗口类别定义了窗口消息处理程序和依据引类别建立的窗口的其它特征。在建立窗口时,要定义一些该窗口所独有的特征。在为程序建立窗口之前,必须首先调用RegisterClass注册一个窗口类别。该函数只需要一个参数,即一个指向形态为WNDCLASS的结构指针。这些结构包括两个指向字符串的字段(LPCTSTR lpszMenuName; LPCTSTR lpszClassName;--ASCII。 LPCWSTR lpszMenuName ;LPCWSTR lpszClassName;--UNICODE)因此结构在WINUSER.H表文件中定义了两种不同的方式(ASCII,UNICODE)在编程过程我们不必去理会!因为在预定义时已经处理过了(WNDCLASSA,WNDCLASSW---都用WNDCLASS就行)! 2、在窗体类定义中最重要的是lpfnWndProc和最后一个字段lpszClassName。
3、窗口类别 他定义了窗口的一般特征,因此可以使用同一窗口类别建立许多不同的窗口,建立窗口时,可能指定有关窗口的更详细的信息。传递给RegisterClass函数的信息会在一个数据结构中设定好,而传递给CreateWindow函数的信息会在函数单独的参数中设定好。
四、消息循环:
Windows为当前执行的每个windows程序维护一个“消息队列”。在发生输入事件后,windows将事件转换为一个消息并将消息放入程序消息队列中。程序通过执行一块称之为消息循环的程序代码从消息队列中取出消息。
在这里我们了解一下下MSG结构:
Typedef struct tagMSG{
HWND hwnd; //接收消息的窗口句柄
UNIT message; //消息标识符WM_XXXX
WPARAM wparam; //32位其含义和数值根据消息的不同而不同
LPARAM lparam;
DWORD time; //消息放入消息队列中的时间
POINT pt; //消息放入消息队列时的鼠标坐标
}
MSG,*PMSG; BOOL GetMessage(
LPMSG lpMsg, // address of structure with message
HWND hWnd, // handle of window
UINT wMsgFilterMin, // first message
UINT wMsgFilterMax // last message
);
这个调用付给windows一个指标,指向名这msg的MSG结构。lpMsg是一个消息地址,hWnd如果为NULL表示程序接收它自己建立的所有窗口的所有消息,Windows用从消息队列中取出的下一个消息来填充消息结构的各个字段。只要从消息队列中取出消息的message字段不为WM_QUIT,GetMessage就传回一个非零值。
BOOL TranslateMessage(
CONST MSG *lpMsg // address of structure with message
);
//将msge结构传给windows,进行一些键盘转换
LONG DispatchMessage(
CONST MSG *lpmsg // pointer to structure with message
);
又将msg结构回传给windows。然后,windows将该消息发送给适当的窗口消息处理程序,让它进行处理。这里就是windows将会调用消息处理程序,也就是这个窗口的wndPro函数。处理完消息之后,WndProc传回到windows,此时window还停留在despatchMessage调用中,在结束dispatchMessage调用的处理之后,windows回到GetMessage调用开始消息循环。
五、窗口消息处理程序:
1、在上面我们介绍了一个窗口的产生过程和动作过程:注册窗口类别,建立窗口,然后在屏幕上显示窗口,程序进入消息循环,然后不断从消息队列中取出消息来处理。
2、实际的动作发生在窗口消息处理程序中。窗口消息处理程序确定了在窗口的显示区域中显示以及窗品怎样响应使用者输入。窗口消息处理程序可任意命名,一个windows程序中可以包含多个窗口消息处理程序。一个窗口消息处理程序总与调用的registerClass注册的特定窗口类别相关联。createWindow函数根据特定窗口类别建立一个窗口。但依据一个窗口类别,可以建立多个窗口。
窗口消息处理程序总是定义为如下形式:
LRESULT CALLBACK WndProc(HWND hwnd,UNIT message,WPARAM wparam,LPARAM lparam)
这四个参数和Msg结构的前四个字段是相同的。
3、程序通常不直接呼叫窗口消息处理程序,消息处理程序通常由windows本身调用。窗口消息处理程序在处理消息时,必须传回窗口消息处理程序不处理的所有消息应传给名为DefWindowProc的Window函数。Wndproc只先择处理三种消息:WM_CREATE,WM_PAINT,WM_DESTROY。 WM_CREATE:处理进行一次窗口初始化。
4、WM_PAINT:当窗口显示区域的一部分或者全部内容必要更新显示或无效时,将由这个消息通知程序。
什么叫无效,无效就是说如果窗口调用updateWindows时例如在窗口类中我们写的style=CS_HREDRAW|CS_VREDRAW这表示在窗口大小改变后就把整个窗口显示内容变为无效。
5、调用updateWindows进行重绘。在对WM_PAINT的处理几乎总是从一个BeginPaint(hwnd,&ps)调用开始,以EndPaint(hwnd,&ps)结束,ps包含一些窗口消息处理程序可以用来更新显示区域的内容。
6、在BeginPaint调用中,如果显示区域的前景还没被删除则由windows来删除,它使用窗口类别的WNDCLASS的结构中的hbrBackground字段中指定的画刷来删除前景。BeginPain返回一个只能在当前区域绘图的设备内容句柄。EndPain释放设备内容句柄。GetClientRect(hwnd,&rect)rect是返回一个窗口显示区域的尺寸。
7、WM_DESTROY:该消息是使用者单击Close按钮或者程序的系统菜单上选择Close时发生的。在这里用的是PostQuitMessage(0)来生效的。该函数在程序的消息队列中插入一个WM_QUIT消息使GetMessage得到一个WM_QUIT的消息,它传回0,终止程序然后执行return msg.wparam;
六、队列化消息与非队列化消息:
Windows给窗口发送消息,这意味着windows调用窗口消息处理程序。但是,windows程序也有一个消息循环,它调用GetMessage从消息队列中取出消息,并且调用dispatchMessage将消息发送给窗口消息处理程序。
消息被分为“队列化的”和“非队列化的”。
1、 队列化的消息:是由windows放入程序消息队列中的。在程序的消息循环中,重新传回并分配给窗口消息处理程序。如键盘消息,WM_PAINT windows消息等。
2、 非队列化的消息在windows调用窗口时直接送给窗口消息处理程序。非队列化消息多来自己于调用windows函数如CreateWindows,键盘或鼠标输入时发出的队列化消息信号,也能在非队列化消息中出现。
3、 队列化的消息被发送给消息队列,而非队列的消息则发送给窗口消息处理程序。
任何发问下,窗口消息处理程序都将获得窗口所有的消息――包括队列和非队列化的消息。窗口消息处理程序是窗口的消息中心。
4、在许多情况下,窗口消息处理程序必须保存它从消息中取得的信息,并在处理另一个消息时使用这些信息。这些信息可以储存在窗口的静态变量或整体变量中。
WINDOWS消息和窗口简介的更多相关文章
- Windows消息机制
Windows的消息系统是由3个部分组成的: · 消息队列.Windows能够为所有的应用程序维护一个消息队列.应用程序必须从消息队列中获取消息,然后分派给某个窗口.· 消息循环.通过这个循环机制应用 ...
- windows消息机制详解(转载)
消息,就是指Windows发出的一个通知,告诉应用程序某个事情发生了.例如,单击鼠标.改变窗口尺寸.按下键盘上的一个键都会使Windows发送一个消息给应用程序.消息本身是作为一个记录传递给应用程序的 ...
- windows消息钩子注册底层机制浅析
标 题: [原创]消息钩子注册浅析 作 者: RootSuLe 时 间: 2011-06-18,23:10:34 链 接: http://bbs.pediy.com/showthread.php?t= ...
- Windows消息机制详解
消息是指什么? 消息系统对于一个win32程序来说十分重要,它是一个程序运行的动力源泉.一个消息,是系统定义的一个32位的值,他唯一的定义了一个事件,向 Windows发出一个通知,告诉应用 ...
- Windows消息机制知识点总结
1.windows消息类型 以下四种,前三种是系统消息,范围在[0x0000, 0x03ff],第四种是用户自定义消息. 1.1 窗口消息 与窗口的内部运作有关,如创建窗口,绘制窗口,销毁窗口等.可以 ...
- Windows消息大全(转)
原链接地址: http://www.cnblogs.com/icebutterfly/archive/2011/08/05/2128864.html 表A-1 Windows消息分布 消息范围说 明 ...
- Windows消息过滤
在C#编程中,经常会遇到一些场景,如禁止鼠标拖动窗体,启用某些快捷键,禁止鼠标移动等.遇到这些需求,可以通过窗体的MouseMove事件,OnDragDrop,OnMove等事件来解决问题, 但是该方 ...
- 【转】windows消息和消息队列详解
转载出处:http://blog.csdn.net/bichenggui/article/details/4677494 windows消息和消息队列 与基于MS - DOS的应用程序不同,Wind ...
- Windows GUI代码与Windows消息问题调试利器
Windows GUI代码与Windows消息问题调试利器 记得很久前有这么一种说法: 人类区别于动物的标准就是工具的使用.同样在软件开发这个行业里面,对于工具的使用也是高手和入门级选手的主要区别,高 ...
随机推荐
- (笔记)Mysql命令delete from:删除记录
delete from命令用于删除表中的数据. delete from命令格式:delete from 表名 where 表达式 例如,删除表 MyClass中编号为1 的记录: mysql&g ...
- mysql 合理创建索引
场景: KEY `index_gscode_f4_f7` (`gscode`,`f4`,`f7`) USING BTREE KEY `index_gscode_f7_f4` (`gscode`,`f7 ...
- lua------------------Unity3D研究院编辑器之打开unity不可识别的文件(十三)
Unity3D研究院编辑器之打开unity不可识别的文件(十三) 雨松MOMO [Unity3D拓展编辑器] 围观8597次 9 条评论 编辑日期:2017-03-02 字体:大 中 小 有些特殊 ...
- (https://www.ibm.com/developerworks/community/forums/html/topic?id=77777777-0000-0000-0000-000014550004)Topic: Caught java.io.CharConversionException. ERRORCODE=-4220, SQLSTATE=null
270002WDPN 3 Posts 0 people l ...
- php 变量定义方法
1.定义常量define("CONSTANT", "Hello world."); 常量只能包含标量数据(boolean,integer,float 和 str ...
- java链接mysql 中文乱码
{转!} 背景: 由于最近在开发一个APP的后台程序,需要Java连接远程的MySQL数据库进行数据的更新和查询操作,并且插入的数据里有中文,在插入到数据库后发现中文都是乱码.网上查了很多教程,最后都 ...
- Asp.net中文本框全选的实现
一.鼠标滑过textbox全选 前台: <asp:TextBox runat="server" onMouseOver="this.focus();this.sel ...
- 图解HTTP学习笔记——简单的HTTP协议
简单的HTTP协议 前言 最近在学习Web开发的相关知识,发现<图解HTTP>这本书通俗易懂.所以花时间学习这本书,并记录下学习笔记. 如上图,我们每天都在浏览网页,一般我们都是在浏览器地 ...
- 杨涛老师MvcPager示例
杨涛老师插件地址:http://www.webdiyer.com/mvcpager 杨涛老师网站上示例写的很详细了,参考他的示例和帮助文档就可以运用的很好了,有经验的同学可以直接参考官方示例. 一.标 ...
- False 'Sharing Violation' Xcopy error message
今天想要将QC的新工具自动拷贝到p4 用户机器上使用,为了避免每次通知大家升级啊!!! 于是,我在程序里调用了bat文件,执行拷贝操作,想在默默的情况下替换更新新版本工具,结果我测试发现没能成功更新版 ...