windows程序设计第一章讲义文档格式.docx
《windows程序设计第一章讲义文档格式.docx》由会员分享,可在线阅读,更多相关《windows程序设计第一章讲义文档格式.docx(16页珍藏版)》请在冰豆网上搜索。
这是通过消息机制(Message)来实现的。
操作系统将每个事件都包装成一个称为消息的结构体MSG来传递给应用程序,参看MSDN。
MSG结构定义如下:
typedefstructtagMSG{
HWNDhwnd;
UINTmessage;
WPARAMwParam;
LPARAMlParam;
DWORDtime;
POINTpt;
}MSG;
解释:
(MSDN是微软提供的程序开发的在线帮助系统,这个系统包括了微软的所有开发语言开发工具的帮助文件,信息很全每年发布4次,一个季度一次。
)
参数1HWNDhwnd;
解释:
handletowindow
新的数据类型,指示窗口的句柄,何谓窗口?
屏幕上的一块矩形区域叫窗口,我们通常的开发的应用程序叫窗口程序,如VISUALC++由许多窗口构成,每个窗口又要起标识,当你去定义一个变量来存储这些标识时,你需要有存储定义这些标识的类型来定义变量,像定义整形变量存放整型数据,定义浮点类型变量存放浮点型数据,要存放窗口标识,需要有窗口句柄来定义这样的变量。
句柄句柄(HANDLE),资源的标识。
操作系统要管理和操作这些资源,都是通过句柄来找到对应的资源。
句柄类似指针,也需要占据一块内存,那么对于资源占据一块内存,我们要所引这块内存,索引资源,就是通过句柄找到的。
按资源的类型,又可将句柄细分成图标句柄(HICON),光标句柄(HCURSOR),窗口句柄(HWND),应用程序实例句柄(HINSTANCE)等等各种类型的句柄。
操作系统给每一个窗口指定的一个唯一的标识号即窗口句柄。
参数2UINTmessage无符号整型,消息就是具体一个消息,比如,按下鼠标左键,左键作为参数传递过来,整数表示方式。
数值不好记忆,微软定义很多宏,以WM开头,windowmessage,如鼠标左键按下LBUTTONDOWN,键盘按键,KEYDOWN
参数3WPARAMwParam;
消息的附加信息
LPARAMlParam;
都是整型。
关于消息的附加信息。
如按下A键,WM_CHAR消息,收到消息,我们并不知道那个字母,要想知道,把字母的ASCII码放到附加参数里,这样我们收到消息通过附加参数知道键盘上字母的ASII码。
MSDN查看WM_CHAR消息
既然WPARAMLPARAM都是整型,为什么要再产生两个新的数据类型?
我们看别人写的程序:
intx,y;
x=30;
y=30;
//x和y既可以用来表示坐标点,也可以用来表示宽度和高度,还可以用来表示身高和体重。
typedefintWIDTH
typedefintHEIGHT
WIDTHx;
HEIGHTy;
//好处:
我们从变量的类型上就可以知道x和y是用来表示宽度和高度。
这就是为什么在WINDOWS中微软为我们定义出这么多新的数据类型,虽然都是整型,
参数5DWORD32位整数,指示消息传递出的时间。
参数6POINT点结构体,x,y坐标,当消息被传递时光标在屏幕的位置。
拷贝,查看MSDN
由上看出消息结构体包含很多信息,它包含消息与哪个窗口相关,消息的具体内容是什么,消息的附加参数,消息投递时间和光标位置。
消息队列:
操作系统为每个应用程序都建立一个消息队列,是一个先进先出的缓冲区,通常是某种变量类型的数组,队列中的每个元素都是一个消息,操作系统将生成的每个消息按先后顺序存放到消息队列,应用程序总是取走队列中的第一个消息,消息取走后,第2条成为第1条,后面的依次前提,应用程序取得消息后就知道用户的操作和程序的变化,例如取得WM_CHAR消息,那么一定是用户输入了字符,并且知道是哪个字符。
应用程序得到消息后就要处理消息,这就是消息响应。
消息响应通过编码来实现,这就是WINDOWS程序的主要代码区,需要编写一些编码,这个过程叫消息响应。
在实现过程可以调用API函数,以便完成特定的功能,例如,我们收到关闭窗口的消息,那么可以调用销毁窗口的函数来关闭窗口,或者用弹出消息框的形式询问用户是否真的关闭窗口。
通过以上分析,我们知道要想开发WINDOWS应用程序,除了要具备良好的C++基础,还需要掌握两方面的东西:
一消息本身,知道不同的消息不同的消息代表的用户操作和程序状态本,。
二。
对于不同消息我们要让操作系统执行特定的功能去响应响应
WINMAIN函数
二,Windows程序的结构
1Windows程序的入口函数
intWINAPIWinMain(
HINSTANCEhInstance,//handletocurrentinstance
HINSTANCEhPrevInstance,//handletopreviousinstance
LPSTRlpCmdLine,//commandline
intnCmdShow//showstate
);
查看MSDN
说明:
参数1HINSTANCEhInstance:
应用程序实例句柄,何谓运行程序实例句柄,即运行中的程序,叫实例,再次运行,有两个实例。
这两个实例需要有个标识,同样需要有一种数据类型来定义变量来存放表示,这个类型就是HINSTANCE
参数2HINSTANCEhPrevInstance:
先前实例句柄,对第2个实例,前一个实例就是这个参数。
兄弟实例
注意:
基于w32的应用程序该参数为空如98,NT下开发不需要此参数,为空。
参数3LPSTRlpCmdLine类型是长指针,指向字符串首地址的指针。
命令行参数,
在DOS下MAIN函数可以有两个参数,argv[],argc[],argv[]指针数组,存放命令行参数,argc[]主要接受存放命令行参数个。
同样在WINDOWS程序中也可以接受命令行参数
运行“notepad1.txt”1.txt就是命令行参数
我们的程序中也可以接收命令行参数,project/settings/debug选项卡下programarguments输入“weixin.txt”,在程序中intWINAPIWinMain开始位置设置断点,然后调试运行。
参数4intnCmdShow显示状态,程序运行时是最大化,最小化或隐藏显示。
WinMain函数是入口点函数,由操作系统调用。
当操作系统启动我们的程序,它会给运行中的程序分配一个实例号,通过参数就传递进来,如果说启动时命令行参数,操作系统会将参数放在第3个参数中,参数都是由操作系统赋值。
2创建一个完整的窗口需要经过下面四个操作步骤:
设计一个窗口类;
注册窗口类;
创建窗口;
显示及更新窗口。
设计窗口WINDOWS已经做好框架,我们只需要填写一部分,如同做填空题一样,这就需要数据结构,结构体
原因:
程序运行时窗口的样式风格可以不一样,如光标在窗口是箭头还是十字,窗口的标题,背景等等,需要我们在设计窗口时就要确定的
产生一个窗口和设计汽车是一样,生产汽车前,我们都需要在图纸上画出汽车的图,雏形,然后起个名字,比如奔腾600,有了图纸照着生产汽车。
类似产生一个窗口也要设计窗口的特征,没有汽车复杂,windows已经给我们设计了窗口应该有的基本属性,我们要做的就跟做填空题一样,把它填写完整。
(查看MSDN)
typedefstruct_WNDCLASS{
UINTstyle;
窗口类的类型,很多。
结合程序看含义-,水平重画和垂直重画,就跟汽车喷漆一样。
WNDPROClpfnWndProc;
指针指向窗口过程,也称回调函数
intcbClsExtra;
类额外数据,操作系统为系统中每个窗口类管理类结构,当程序注册窗口类,可让系统为追加额外内存空间,我们称为类的附加内存,由属于这种窗口类的所有窗口共享,cbClsExtra,通常为0
intcbWndExtra;
操作系统为系统中每个窗口管理类结构,当程序注册窗口,可让系统为追加额外内存空间,我们称为窗口的附加内存
HANDLEhInstance;
实例号,设计窗口类,要知道属于哪个
HICONhIcon;
图标句柄,通过LoadIcon加载图标
HCURSORhCursor;
HBRUSHhbrBackground;
LPCTSTRlpszMenuName;
LPCTSTRlpszClassName;
新类的名字
}WNDCLASS;
UINTstyle
参数1UINTstyle;
在我们的程序中经常要用到一类变量,这个变量里的每一位(bit)都对应某一种特性。
当该变量的某位为1时,表示有该位对应的那种特性,当该位为0时,即没有该位所对应的特性。
当变量中的某几位同时为1时,就表示同时具有几种特性的组合。
一个变量中的哪一位代表哪种意义,不容易记忆,所以我们经常根据特征的英文拼写的大写去定义一些宏,该宏所对应的数值中仅有与该特征相对应的那一位(bit)为1,其余的bit都为0。
我们使用gotodefinition就能发现CS_VREDRAW=0x0001,CS_HREDRAW=0x0002,CS_DBLCLKS=0x0008,CS_NOCLOSE=0x0200。
他们的共同点就是只有一位为1,其余位都为0。
如果我们希望某一变量的数值既有CS_VREDRAW特性,又有CS_HREDRAW特性,我们只需使用二进制OR(|)操作符将他们进行或运算相组合,如style=CS_VREDRAW|CS_HREDRAW|CS_NOCLOSE。
如果我们希望在某一变量原有的几个特征上去掉其中一个特征,用取反(~)之后再进行与(&
)运算,就能够实现,如在刚才的style的基础上去掉CS_NOCLOSE特征,可以用style&
~CS_NOCLOSE实现。
参数2WNDPROClpfnWndProc;
回调函数的原理是这样的,当应用程序收到给某一窗口的消息时(还记得前面讲过的消息通常与窗口相关的吗?
),就应该调用某一函数来处理这条消息。
这一调用过程不用应用程序自己来实施,而由操作系统来完成,但是,回调函数本身的代码必须由应用程序自己完成。
对于一条消息,操作系统到底调用应用程序中的哪个函数(回调函数)来处理呢?
操作系统调用的就是接受消息的窗口所属的类型中的lpfnWndProc成员指定的函数。
每一种不同类型的窗口都有自己专用的回调函数,该函数就是通过lpfnWndProc成员指定的。
举例:
汽车厂家生产汽车好比应用程序创建窗口,用户使用汽车好比操作系统管理窗口,某种汽车在销售前就指定好了修理站(类似回调函数),当用户的汽车出现故障后(类似窗口收到消息),汽车用户(类似操作系统)自己直接找到修理站去修理,不用厂家(类似应用程序)亲自将车送到修理站去修理,但修理站还得由厂家事先建造好。
参数3intcbClsExtra;
类额外数据,操作系统为系统中每个窗口类管理类结构,当程序注册窗口类,可让系统为追加额外内存空间,我们称为类的附加内存,由属于这种窗口类的所有窗口共享,cbClsExtra,通常为0,赋为0不是说内存为0,而是我们不需要这个参数。
参数4intcbWndExtra;
参数5HANDLEhInstance;
实例号,设计窗口类,要知道属于哪个应用程序实例,因此,当我们对实例赋值时,从winmain中的这个参数,传递过来。
参数6HICONhIcon;
图标句柄,通过LoadIcon加载图标,那图标句柄如何赋值?
查看MSDN:
HICONLoadIcon(
HINSTANCEhInstance,//handletoapplicationinstance
LPCTSTRlpIconName//namestringorresourceidentifier
当取标准图标时,null。
在windows编程中,当需要某些功能的函数时,可以凭感觉根据语义组合一些名称,如果,createwindow,destroywindow等。
当然符合不符合需要还得去看看含义。
时间长就记住了。
参数7HCURSORhCursor;
光标句柄,用LoadCursor与LoadIcon接近,查MSDN
HCURSORLoadCursor(
LPCTSTRlpCursorName//nameorresourceidentifier
参数8HBRUSH画刷句柄hbrBackground=GetStockObject()
查MSDN:
HGDIOBJGetStockObject(
intfnObject//stockobjecttype
可以修改例子中的参数,改为白色背景。
就跟装修房子刷墙一样,刷什么颜色,墙就是什么颜色。
要有强制类型转换。
参数9LPCTSTRlpszMenuName;
指向常量字符串的指针,指向菜单的名字
参数10LPCTSTRlpszClassName;
新类的名字,
注册窗口类
设计完窗口类后,要向操作系统注册,RegisterClass(&
类名)就如设计完汽车并不能直接生产,要上报进行注册。
查MSDNATOMRegisterClass(
CONSTWNDCLASS*lpWndClass//classdata窗体类结构体的指针
创建窗口
首先要定义一个句柄,因为创建窗口和生产汽车一样,生产多少汽车应该有数,同样创建窗口之后,哪个窗口的标识应该保存下来,有哪些窗口。
函数:
CreateWindow(“类名”),查msdn
CreateWindow(//创建具体窗口句柄
参数1LPCTSTRlpClassName//窗口类名,必须与注册时的名字相同,”weixin”,如果换一下,运行后看到,窗口没有产生,但是程序还是运行,从任务管理器可以看到winmain
原因?
参数2LPCTSTRlpWindowNameszTitle,//窗口名字,即标题栏内容
参数3DWORDdwStyle窗口类型即//显示方式,实例中的该参数点右键到gotodefinition
#defineWS_OVERLAPPEDWINDOW(WS_OVERLAPPED|\重叠窗口,有一个标题栏和边框
WS_CAPTION|\有标题栏
WS_SYSMENU|\
WS_THICKFRAME|\创建具有可调边框的
WS_MINIMIZEBOX|\
WS_MAXIMIZEBOX)
这就是以前说的二进制位表示特征,想要某位时置为1,否则置为假,我们可以看看上面这些宏,都是只有1位为1.演示去掉最大化按钮
参数4intx,CW_USEDEFAULT,//初始的X位置
参数5inty,CW_USEDEFAULT,//初始的Y位置
参数6intnWidthCW_USEDEFAULT,/窗口的宽度
参数7intnHeight,//窗口的高度
参数8HWNDNULL,//父窗口句柄,必须与注册时的名字相同
参数9HMENUNULL,//窗口菜单句柄,必须与注册时的名字相同
参数10HINCTANCEhInstance,//实例句柄,必须与程序使用的名字相同
参数11LPVOIDlpParamNULL//窗口创建时作为WMCREATE消息的lpParam附加消息通过结构体传入窗口的数据的指针
当窗口创建好后,就好像汽车造好,要开新闻发布会推出去一样,也需要显示出来。
显示调用函数ShowWindow(hWnd,nCmdShow);
其中第1个参数是窗口句柄,第2个参数是窗口初始显示的形式,nCmdShow参数说明是最大化显示还是最小化显示,应用程序窗口是活动的窗口还是图标,它可置为以“SW_”开始的任何常量,这些常量在windows.h中定义,在WinMain函数中,也是nCmdShow常量决定如何显示主窗口。
在用nCmdShow作为参数调用函数ShowWindow之后,WinMain还要调用UpdateWindow函数。
更新窗口。
不影响窗口更新,可有可无。
3消息循环
MSGmsg;
//消息
MSGmsg;
while(GetMessage(&
msg,NULL,0,0))
{
TranslateMessage(&
msg);
转化翻译消息,它的作用是对取到的消息对进行转换,什么样的消息对?
当我们在键盘上按下某个按键时,系统会发出WM_KEYDOWN和WM_KEYUP的消息,并且参数中提供的键盘上刚才按键的扫描码,但有时候用户按下的某个键,我们想得到的是表示用户输入的字符的消息,也就是键盘上的某个字母,在消息的补充参数,就是附加参数中提供字母按键的ASCII码。
TranslateMessage能够将WM_KEYDOWN和WM_KEYUP这样的消息对转换为一个WM_CHAR消息,并且将转化后的新消息投递到消息队列中,这个转换过程不会影响原来的消息,只能增加一个消息。
DispatchMessage(&
是将我们收到的消息传给回调函数去处理。
我们可以理解为将这个消息路由给操作系统,操作系统去调用窗口过程函数,即回调函数进行处理。
};
}
BOOLGetMessage(
LPMSGlpMsg,//messageinformation,指向消息结构体的指针,out标识表明在传参时不需要给结构体内部数据成员进行初始化,我们只需要定义结构体的变量,将它的地址放在这就可以了。
那么通过函数调用会自动填充消息结构体内部的成员变量。
当
HWNDhWnd,//handletowindow获取哪个窗口消息,NULL,可以获取所有消息
UINTwMsgFilterMin,//firstmessage消息队列中有很多消息,有的我们感兴趣,有的不,也可以指定消息范围,指定消息最小值。
WM_KEYFIRST指定键盘第一个消息,WM_MOUSEFIRST来指示第一个鼠标消息。
UINTwMsgFilterMax//lastmessage消息最大值,
如果这两个值都设为0,就没有范围过滤了,所有消息都感兴趣。
这个消息返回值是BOOL,当取出一个消息时返回真,队列中只要有消息,返回值就是真,就构成循环。
回顾消息循环
我们再看一下窗口过程函数。
MSDN查看WNDCLASS
LRESULTCALLBACKWindowProc(
HWNDhwnd,//handletowindow
UINTuMsg,//messageidentifier
WPARAMwParam,//firstmessageparameter
LPARAMlParam//secondmessageparameter
注意:
:
这是C++程序开发时规定的格式,不能改的,函数名可以变,但是参数类型不能变,名字可以随便取。
LRESULT是LONG型的,长整型。
这4个参数和消息结构体的前4个参数比较类似,实际上是一样的,当DispatchMessage(&
msg)执行时,把消息的前4个参数传递过来,消息的后2个参数没有用到,消息投递的时间和坐标在屏幕上的位置。
每当有消息产生时,都要调用窗口过程函数,因此要判断是哪个消息。
RESULTCALLBACKWinSunProc(
UINTuMsg,