WINDOWS程序内部运行原理
2012-06-05 16:00
399 查看
API(Applicationprogramminginterface)应用程序编程的接口
MSG(message)消息结构体
操作系统将每一个事件包装成一个称为消息MSG的结构体传递给应用程序
MSG的结构定义如下:(windowsuserinterface:platformsdk)
TypedefstructtagMSG{
HWNDhwnd;窗口的句柄句柄为资源的标识,按类型分为HICON/HCURSOR/HWND/HINSTANCE
UINTmessage;无符号整形具体的消息;用宏来表示数值WM开头的为宏WM_*
WPARAMwParam;整型.关于消息的附加信息.WM_CHAR消息字符的代码ASCII;
LPARAMlParam;;整型.关于消息的附加信息.PARAM=PARAMETER参数,参量
DWORDtime;;WORD16位的整数DWORD=DoubleWORD32位整数.指定消息的投递时间.
POINTpt;当消息被投递的时候光标太屏幕上的坐标;
}MSG,*PMSG;
POINT为一个结构体定义了点x坐标y坐标
TypedefstructtagPOINT{
LONGx;
LONGy;
}POINT,*PPOINT;结束时间2009年2月19日4:57:49
2009年2月20日13:18:04(第二次)
消息:消息本身,特定的消息响应
WINMAN函数
入口点函数类似于C的MAN函数
intWINAPIWinMan(
HINSTANCEhInstance,//handletocerrentinstance实例(资源)的句柄,当前运行的句柄;
HINSTANCEhPrevinstance,//handletopreviousinstance先前实例的句柄,可能为空,兄弟的实例句柄
win32下这个实例总是为空(win98、2000等);
LPSTRlpCndLine,//commandlineLP->LONGPOINTER长指针,ETRING字符串,指向字符
串的指针;类似于CHAR*命令行参数
intnCmdSbow//showstate显示的状态,比如窗口的大小全屏
);//这个分号在具体的编程过程中是不要的,需要删除掉。
DOS下的MAIN函数可以接收2个参数:ARJc、ARJv.ARJc用来接受、存放命令行参数的个数,ARJv指针数组用来存放命令行参数,同样的在windows程序当中他也可以接收命令行的参数.
WINAPI:Callingconventionforsystemfunctions.
2009年2月24日15:10:15(第三次)
窗口的创建。创建一个完整的窗口需要经过下面四个操作步骤:
l设计一个窗口类;
l注册窗口类;
l创建窗口;
l显示及更新窗口;
1、设计窗口。设计一个窗口就是设计一个窗口类,他是一个结构体
WNDCLASS
Typedefstruct_WNDCLASS{//MSDN--WNDCLASS
UINITstyle;style//(ClassStyles=CS)指定一个类的类型,这里是窗口类的类型;OR(|),与(&),取反(~),
赋值:wndclass.style=CS_HREDRAW|CS_VREDRAW(水平和垂直重画)
WNDPROClpfnWndProc;窗口过程。Lp=longpointer.fn=functionProc=procedure用来接收一个函数指针。
回掉函数(lpfnWndProc:Pointertothewindowprocedure.Youmustusethe
CallWindowProc[回调window进程]functiontocallthewindowprocedure.Formore
information,seeWindowProc.)。这里我们会把一个窗口名赋给他(函数名相当于一个
函数的指针),即在设计的时候就确定了一个回调函数,这里给他赋了WinSunProc
这个函数,当然WinSunProc函数具体内容是什么需要程序员自己去编写。
intcbClsExtra;windows程序会为每一个类管理一个内部数据结构。类附加内存,通常情况下设为零.(=0)
类额外的一个数据。(附加字节数)
intcbWndExtra;windows程序会为每一个窗口管理一个内部数据结构。窗口附加内存,通常情况下也设
置为零。以上两个分配额外的内存,通常我们不需要所以设置为零。(=0)
窗口类额外的一个数据。通常赋值为零表示我们不需要额外的内存,但是如果我们需要则额外的内存空间会自己变为零。这里设置为零就是=0,不同于其他的设置为NULL,因为这里是整数。
HANDLEhInstance;代表我们当前程序的实例号。操作系统分配,直接通过WinMain的形参传递过来。
代表了我们当前应用程序的实例号,我们在设计窗口类的时候需要知道他是代表那
个应用程序实例的。从bInstance获得这个程序。
HICONhIcon;图标的句柄。用LoadIcon这个函数来赋值hIcon=LoadIcon(……)
HCURSORhCursor;光标句柄。用LoadCursor这个函数来赋值hCursor=LoadCursor(……)
HBRUSHhbrBackground;画刷的句柄,用的是GetStockObject()这么一个函数
LPCTSTRlpszMenuName;LP=longpoint(32位),CT=constant常量,STR=string.用来设定菜单的名字;
可以直接设置为空NULL,如:lpszMenuNam=NULL;
(如果这个类型不认识可以用datatype这个来查看不是SDK)
LPCTSTRlpszClassName;设置一个类的名字(注册窗口的时候会用到),窗口设计完成之后需要给窗口取一
个名字。
}WNDCLASS,*PWNDCLASS;
这个函数是系统定义好了格式,我们只需要拿过来用,直接对参数进行赋值。
WindowProc回调函数,窗口过程函数
LRESULTCALLBACKWindowProc(//CALLBACK:窗口过程函数前边定义的时候加了一个CALLBACK
#defineCALLBACK_stdcall
CALLBACK就是一个类型的定义
LRESULT就是一个长整型,返回一个结果码。
HWNDhwnd,//handletowindow
UINTuMsg,//messageidentifier
WPARAMwParam,//firstmessageparameter
LPARAMlParam//secondmessageparameter
);
DefWindowProc回调函数,窗口过程函数
LRESULTDefWindowProc(
HWNDhWnd,//handletowindow
UINTMsg,//messageidentifier
WPARAMwParam,//firstmessageparameter
LPARAMlParam//secondmessageparameter
);
_stdcall和_cdecl这是两种调用的约定,_stdcall标准调用约定,_cdecl为c语言的调用约定。标准调用约定也是PASCAL调用约定,dephi就是pascal调用约定。_stdcall和_cdecl在参数的传递顺序和堆栈的清除这两个方面有差异。Vc++默认的调用约定选项是_cdecl.需要时要经行定义。也可以通过Project->ProjectSettings->C/C++->Callingconvention:中选择_stdcall这种方式来设定。
LoadIcon
HICONLoadIcon(
LPCTSTRlpIconName//namestringorresourceidentifier当第一个参数取值为NULL时第二个参数可以选择
);
如:LoadIcon(NULL,IDI_ERROR)//ID=identifier
LoadCursor
HCURSORLoadCursor(
HINSTANCEhInstance,//handletoapplicationinstance应用程序实例的一个句柄,如果想用一个标准的光标
这个参数需要设置为NULL
LPCTSTRlpCursorName//nameorresourceidentifier同样当前一个参数设置为NULL是这个可以选择微
软提供的光标函数。
);
GetStockObject()获取一个笔、画刷、调色板、的句柄。
HGDIOBJGetStockObject(
intfnObject//stockobjecttype
);
*=(HBRUSH)GetStockObject(WHITE_BRUSH);//(HBRUSH)这里是一个强制转换。因为GetStockObject返回的是一个GetStockObject的类型,而hbrBackground定义的为HBRUSH的类型,C++语言是一个强类型语言,对函数的类型要求的很严。强制类型转换必须要有可比性,即两个类型可以转换,这种转换在编译的时候不会报错但是在执行的时候会出现错误。
2、注册。窗口设置完之后需要对其进行注册(向操作系统注册),RegisterClass()窗口的注册函数
RegisterClass
ATOMRegisterClass(
CONSTWNDCLASS*lpWndClass//classdata窗口类结构体的指针直接用定义的窗口类结构体的变量加上取
地址符&
);
3、创建一个窗口。
首先需要定义一个句柄(HWND),利用这个句柄的变量保存我们新创建的窗口的标识。然后利用CreateWindow函数来进行窗口的创建。
HWNDhwnd;
Hwnd=CreateWindow(……);
CreateWindow
HWNDCreateWindow(
LPCTSTRlpClassName,//registeredclassname注册的类名,设计窗口类的时候指定的名字,如果名称与开始
定义的不一样那么WinMain程序会运行但是窗口是不回产生的。创建窗口的时候一
定要基于一个已经注册之后的窗口类名来设定
LPCTSTRlpWindowName,//windowname窗口的名字,即我们产生这个窗口的时候他的标题。在标题栏上显
示的窗口的名字,标题栏是一条蓝色的在窗口顶端的长条
DWORDdwStyle,//windowstyle窗口的类型,要和刚才的类的类型区分开来。(WS)如:
WS_OVERLAPPEDWINDOW
intx,//horizontalpositionofwindow窗口的水平坐标;CW_USEDEFAULT,如果x设置为
CW_USEDEFAULT(系统缺省坐标)那么y坐标值就会被忽略。另外屏幕坐标左上角为
原点(0,0)而数学中的为左下角是原点
inty,//verticalpositionofwindow窗口的垂直坐标;x,y显示的时候窗口的左上角的x,y坐标.
intnWidth,//windowwidth窗口的宽度,CW_USEDEFAULT用的是系统缺省的宽度和高度。同样
如果用CW_USEDEFAULT则nHeight会被忽略。
intnHeight,//windowheight窗口的高度
HWNDhWndParent,//handletoparentorownerwindow窗口的句柄,这里指的是父窗口的句柄,如果程
序只有1个窗口没有父窗口则可以设置为空(NULL)
HMENUhMenu,//menuhandleorchildidentifier菜单的句柄。如果用不倒菜单那么我们可以把菜单句柄
设置为空(NULL);
HINSTANCEhInstance,//handletoapplicationinstance当前应用程序实例的句柄,通过WinMain函数传递过
来,直接把实例号hInstance,复制过来就可以了
LPVOIDlpParam//window-creationdata当窗口创建的时候都会有WM_CREATE这个消息产生,
作为WM_CREATE的附加消息有两个由lParam的消息内容传进来。指向
CREATESTRUCT结构体,该结构题的lParam参数是通过创建窗口时产生的
WM_CREATE消息时产生的附加消息中的lParam参数传递过来的,即:作为
WM_CREATE附加消息lParam传递过来的参数数据的指针。如果一个应有实例
CreateWindow创建一个多文档multipledocumentinterface(MDI)界面的窗口该函数
lpParam指针指向结构体CLIENTCREATESTRUCT。
);
WS_OVERLAPPEDWINDOW
WS_OVERLAPPEDWINDOW(
WS_OVERLAPPED|//标识产生一个层叠的窗口,层叠的窗口就是有一个标题栏还有一个边框
WS_CAPTION|//创建一个有标题栏的窗口
WS_SYSMENU|//创建一个带有系统菜单的窗口
WS_THICKFRAME|//创建一个具有可调边框的窗口
WS_MINIMIZEBOX|//创建一个具有最小化按钮的窗口
WS_MAXIMIZEBOX//创建一个具有最大化按钮的窗口
).SameastheWS_TILEDWINDOWstyle.
上边的特征都是有二进制位表示有无。
CREATESTRUCT
typedefstructtagCREATESTRUCT{
LPVOIDlpCreateParams;
HINSTANCEhInstance;
HMENUhMenu;
HWNDhwndParent;
intcy;
intcx;
inty;
intx;
LONGstyle;
LPCTSTRlpszName;
LPCTSTRlpszClass;
DWORDdwExStyle;
}CREATESTRUCT,*LPCREATESTRUCT;
WM_CREATE:WindowProc
LRESULTCALLBACKWindowProc(
HWNDhwnd,//handletowindow
UINTuMsg,//WM_CREATE
WPARAMwParam,//notused
LPARAMlParam//creationdata(LPCREATESTRUCT)
);
4、显示、更新窗口
窗口创建完之后需要显示出来。显示窗口调用函数为:ShowWindow
ShowWindow指定窗口的显示状态
BOOLShowWindow(
HWNDhWnd,//handletowindow窗口的句柄,即要显示的是哪一个窗口,把窗口的标识赋给他
intnCmdShow//showstate窗口的显示状态。如:最大化显示,最小化显示等。(SHOW_SHOWNORMAL
正常化显示)
);
UpdateWindow
显示完窗口之后我们还需要调用一个函数,即为更新窗口的函数,UpdateWindow。当然这个函数如果不添加也不影响窗口的产生。
BOOLUpdateWindow(
HWNDhWnd//handletowindow窗口句柄。
);
消息循环,这里最重要的环节。
首先用MSG结构体定义了一个消息结构体的变量。用While循环对GetMessage进行循环。GetMessage调用消息队列中的消息。
MSGmsg;
While(GetMessage(&msg,NULL,0,0))//当GetMessage取到的消息不是WM_QUIT时执行While循环。
{
TranslateMessage(&msg);//转换消息,翻译消息。对取到的消息对进行转换。当我们按下某一个按键
的时候系统将会产生一个WM_KEYDOWN和WM_KEYUP这样两个的消
息,并提供一个按键的虚拟的扫描码。TranslateMessage将WM_KEYDOWN
和WM_KEYUP消息对转换成ASCII码WM_CHAR消息,并将转换后的消息
投递到消息队列中,转换过程中不会影响原来的消息只会产生一个新的消
息。如果不用这个函数那么我们不可能收到WM_CHAR消息的。
DispatchMessage(&msg);//将收到的消息(TranslateMessage(&msg)转换的消息)传递到窗口的回调函数
即窗口的过程函数中处理,即将消息路由给了操作系统,然后操作系统去调
用窗口过程函数,即我们在设计窗口类WNDCLASS的时候设计的窗口过程
函数WNDPROClpfnWndProc所设定的回调函数进行处理。
}
GetMessage
BOOLGetMessage(//如果返回值是WM_QUIT则返回一个零值,如果返回的不是WM_QUIT则返回的是一
个非零值。
LPMSGlpMsg,//messageinformation[out]PointertoanMSGstructurethatreceivesmessage
informationfromthethread's(线程)messagequeue.消
息结构体的指针。
HWNDhWnd,//handletowindow句柄表示想要获取那个窗口的消息,如果设置为NULL,标识
我们要获取属于这个调用线程的任何窗口的消息
UINTwMsgFilterMin,//firstmessage指定消息的最小的消息值
UINTwMsgFilterMax//lastmessage指定一个消息的最大的消息值,即最后一个消息(MSDN有一
个错误他将last写成first)如果这两个都设置为零那么
GetMessage就没有对消息进行过滤而是针对所有的消息,没有
范围的过滤。
);
[out]->表明我在对lpMsg传参的时候不要对内部成员经行初始化,只需要定义一个结构体的变量将他的地址放在哪里,通过函数的调用他会自动的填充消息结构体内部的成员变量。
GetMessag返回的是一个BOOL型的值。当从消息队列中取回消息是返回值为真,当消息队列中始终都有的消息的时候则返回的值始终都是真。
TranslateMessage
BOOLTranslateMessage(
CONSTMSG*lpMsg//messageinformation
);
DispatchMessage
LRESULTDispatchMessage(
CONSTMSG*lpmsg//messageinformation
);
窗口过程函数(WNDPROClpfnWndProc指定的函数),即回调函数里边的代码
这里设定的窗口调用函数的名称为WinSunProc.怎么知道WinSunProc是什么样的格式呢?选择查看的方式如下:
WNDCLASS->PlatformSDK:WindowsUserInterface->选择seeWindowProc中的WindowProc->我们可以看到函数的形式规定的形式(函数名称可以更改,但是参数的类型不能更改但参数的名字可以更改):
LRESULTCALLBACKWindowProc(//LRESULT就是一个LONG型CALLBACK后边将介绍
HWNDhwnd,//handletowindow窗口的句柄
UINTuMsg,//messageidentifier消息的标识
WPARAMwParam,//firstmessageparameter消息的附加参数
LPARAMlParam//secondmessageparameter消息的第二个附加参数
);
实际上是将消息结构体的前四个参数作为参数传递给WindowProc(这个名称用户可以自己更改)
sprintf:printf在c语言中是想屏幕中输出内容,sprintf是c语言中的一个库函数,是格式化一个文本到内存buffer中,即一个内存区中.指定的区域中打印一些数据的。如下:
charszChar[20];
sprintf(szChar,”charis%d”,wParam);//将wParam的ASCII码的消息以%d的格式格式化到szChar这个字符数组
当中。
MessageBox(hwnd,szChar,”weixin”,MB_ok);//MessageBox用来弹出一个消息框.
MessageBox
intMessageBox(
HWNDhWnd,//handletoownerwindow消息框被创建那个窗口拥有这个消息框的句柄
LPCTSTRlpText,//textinmessagebox消息显示的文本
LPCTSTRlpCaption,//messageboxtitle消息的标题,即在消息的蓝框中的内容显示
UINTuType//messageboxstyle消息框的类型,如MB_OK表示消息框包含了一个OK按键
的button按钮。MB_OK定义的是”0”,我们可以直接用”0”代
替MB_OK.
);
文字的输出
HDChDC;//HDC是一个句柄,DC(devicecontext设备上下文)的句柄。DC是系统内部维护的数据结构占
内存。
hDC=GetDC(hwnd);//获取函数hDC的句柄
TextOut(hDC,0,50,”计算机编程语言培训”,strlen(“计算机编程语言培训”));//文本输出的函数,在窗口上输出一个
文本。
ReleaseDC(hwnd,hDC);//释放DC.如果我们使用了一个DC而没有在使用完之后释放DC那么会造成内存的泄
露。GetDC和ReleaseDC是一对。
HDC:
Handletoadevicecontext(DC).
GetDC
HDCGetDC(
HWNDhWnd//handletowindow窗口的句柄。跟那个窗口相关的DC,利用DC画图的时候就画在那个窗口
上。
);
TextOut
BOOLTextOut(
HDChdc,//handletoDCDC的句柄
intnXStart,//x-coordinateofstartingposition开始x坐标位置
intnYStart,//y-coordinateofstartingposition开始y坐标位置
LPCTSTRlpString,//characterstring输出文本内容
intcbString//numberofcharacters输出文本字符数量。用c的strlen.
);
WM_PAINT当窗口重绘的时候就会发送这个消息。当窗口从无到有的时候会使用这个函数,同时在我们在调试运行时设置的断点中不能在WM_PAINT中设置,如果这样做了那么窗口将会永远不会产生。前面说过当我们窗口的水平或者垂直坐标发生改变的时候窗口都会重绘,重绘就是通过发送WM_PAINT这个消息来完成的。
CaseWM_PAINT://窗口的绘画,下边为响应消息的设置
hdc=BeginPaint(hwnd,&ps);//获取DC的句柄,这个DC是在选择语句上边预定的。
TextOut(hdc,0,0,”北京维新科学技术培训中心”,strlen(”北京维新科学技术培训中心”));//
EndPaint(hwnd,&ps);//释放DC.BeginPaint和EndPaint是一对,并且只能在WM_PAINT上使用。
break;
BeginPaint
HDCBeginPaint(
HWNDhwnd,//handletowindow窗口的句柄
LPPAINTSTRUCTlpPaint//paintinformation设置PAINTSTRUCT结构体的指针。
);
BeginPaint准备为指定的窗口进行绘画,BeginPaint函数自动填充PAINTSTRUCT结构体,PAINTSTRUCT结构体不需要我们来维护由系统内部lpPaint来维护
PAINTSTRUCT
typedefstructtagPAINTSTRUCT{
HDChdc;
BOOLfErase;
RECTrcPaint;
BOOLfRestore;
BOOLfIncUpdate;
BYTErgbReserved[32];
}PAINTSTRUCT,*PPAINTSTRUCT;
WM_CLOSE
caseWM_CLOSE://当我们点击关闭按钮的时候会产生这样的消息。
if(IDYES==MessageBox(hwnd,”你是否要退出程序?”,”weixin”,MB_YESNO))//由MessageBox返回一个ID*
的参数,判断该返回参数与IDYES的比较情况。
{
DestroyWindow(hwnd);//销毁窗口
}
break;
IDYES是一个常量,我们在程序设计的是有有一个小经验:在if语句判断的时候一般把常量放在前边。这样做方便我们找到错误。如:if(x=1)和if(1=x),这两种是在我们输入的时候容易出错的两种情况,第二种在编译的时候就会报错,而第一种x=1是“真”,编译的时候不会报错。
DestroyWindow
BOOLDestroyWindow(
HWNDhWnd//handletowindowtodestroy//要被销毁的窗口的句柄。
);
DestroyWindow这个函数会发送WM_DESTROY和WM_NCDESTROY这两个消息到消息队列中。
WM_DESTROY
CaseWM_DESTROY
PostQuitMessage(0);
Break;
小经验:
caseWM_CLOSE:
if(IDYES==MessageBox(hwnd,”你是否要退出程序?”,”weixin”,MB_YESNO))
{
DestroyWindow(hwnd);
}
break;
CaseWM_DESTROY
PostQuitMessage(0);
Break;
这个程序片段的意思是当MessageBox弹出后用户选择YES时,if语句为真,执行DestroyWindow(hwnd)关掉窗口,同时产生WM_SESTROY和WM_NCDESTROY两个消息,在caseWM_DESTROY中将关掉程序执行的进程,同时程序结束。
caseWM_CLOSE:
DestroyWindow(hwnd);
break;
CaseWM_DESTROY
if(IDYES==MessageBox(hwnd,”你是否要退出程序?”,”weixin”,MB_YESNO))
{
PostQuitMessage(0);
}
Break;
这个程序片段与上边的略有不同,但是执行结果则是完全不一样的。当执行WM_CLOSE时程序会首先执行DestroyWindow(hwnd)关掉窗口产生WM_SESTROY和WM_NCDESTROY两个消息到消息对立中,这个时候响应消息WM_DESTROY,开始执行if的判断,此时才弹出MessageBox的消息对话框,如果用户选择的是否,那么消息对话框关闭了,if语句为否不执行其中的PostQuitMessage(0)也就无法产生WM_QUIT消息,此时窗口关闭了,但是程序没有终止而是在后台中运行。
PostQuitMessage
VOIDPostQuitMessage(//产生WM_QUIT这个消息
intnExitCode//exitcode
);
default
在写消息循环的时候default语句是必不可少的,是对缺省的没有定义的消息由系统进行默认的响应,如果没有那么那些没有定义的消息将会缺少归宿,并且窗口可能不能正常的显示,但是程序还是在后台运行。
deault:
returnDefWindowProc(hwnd,uMsg,wParam,lParem);
return0;
函数的正常返回值,返回一个”0”值。
编写一个程序:
新建一个project:File->New…->Projects->Win32Application|Location:存放地址|Project
name:工程名字->OK->step1:Anemptproject->finish->ok.
新建:File->New…->Files->c++sourcefile(c++源文件)|file:同样的名字->ok.
既然我们编写的是windows程序,所以就要包含#include<windows.h>windows头文件,还有一个就是c语言的头文件#include<stdio.h>原因是我们要用到c语言的库函数。
--------------------------------------------------------------------------------------------------------------------------------------
#include<windows.h>
#include<stdio.h>
LRESULTCALLBACKWinSunProc(//可以通过MSDN查找,不过函数的名称要根据自己的需要修改。
HWNDhwnd,//handletowindow
UINTuMsg,//messageidentifier
WPARAMwParam,//firstmessageparameter
LPARAMlParam//secondmessageparameter
);//函数原型声明
intWINAPIWinMain(
HINSTANCEhInstance,//handletocurrentinstance
HINSTANCEhPrevInstance,//handletopreviousinstance
LPSTRlpCmdLine,//commandline
intnCmdShow//showstate
)
{
WNDCLASSwndcls;
wndcls.cbClsExtra=0;
wndcls.cbWndExtra=0;
wndcls.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
wndcls.hCursor=LoadCursor(NULL,IDC_CROSS);
wndcls.hIcon=LoadIcon(NULL,IDI_QUESTION);
wndcls.hInstance=hInstance;
wndcls.lpfnWndProc=WinSunProc;
wndcls.lpszClassName="liqiang2009";
wndcls.lpszMenuName=NULL;
wndcls.style=CS_HREDRAW|CS_VREDRAW;
RegisterClass(&wndcls);
HWNDhwnd;
hwnd=CreateWindow("liqiang2009","一个简单的窗口",WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,
NULL,NULL,hInstance,NULL);
ShowWindow(hwnd,SW_SHOWNORMAL);
UpdateWindow(hwnd);
MSGmsg;
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return0;
}
LRESULTCALLBACKWinSunProc(
HWNDhwnd,//handletowindow
UINTuMsg,//messageidentifier
WPARAMwParam,//firstmessageparameter
LPARAMlParam//secondmessageparameter
)
{
switch(uMsg)
{
caseWM_CHAR:
charszChar[20];
sprintf(szChar,"您所输入的按键值为:%d或%c",wParam,wParam);
MessageBox(hwnd,szChar,"按键值",0);
break;
caseWM_LBUTTONDOWN:
MessageBox(hwnd,"您按下了鼠标的左键","按键值",0);
HDChdc;
hdc=GetDC(hwnd);
TextOut(hdc,0,25,"HDChdc",strlen("HDChdc"));
ReleaseDC(hwnd,hdc);
break;
caseWM_PAINT:
HDChDC;
PAINTSTRUCTps;
hDC=BeginPaint(hwnd,&ps);
TextOut(hDC,0,0,"重刷的时候不会被去掉的文字",strlen("重刷的时候不会被去掉的文字"));
EndPaint(hwnd,&ps);
break;
caseWM_CLOSE:
if(IDYES==MessageBox(hwnd,"是否真的退出?","询问窗口",MB_YESNO))
{
DestroyWindow(hwnd);
}
break;
caseWM_DESTROY:
PostQuitMessage(0);
break;
default:
returnDefWindowProc(hwnd,uMsg,wParam,lParam);
}
return0;
}
------------------------------------------------------------------------------------------------------------------------------------------
BuildLog
--------------------Configuration:WinMain-Win32Debug--------------------
CommandLines
Creatingtemporaryfile"C:/DOCUME~1/Li/LOCALS~1/Temp/RSPFE.tmp"withcontents
[
/nologo/MLd/W3/Gm/GX/ZI/Od/D"WIN32"/D"_DEBUG"/D"_WINDOWS"/D"_MBCS"/Fp"Debug/WinMain.pch"/YX/Fo"Debug/"/Fd"Debug/"/FD/GZ/c
"D:/soft-教程/vc++/孙鑫课件/lessons/lesson1/WinMain/WinMain.cpp"
]
Creatingcommandline"cl.exe@C:/DOCUME~1/Li/LOCALS~1/Temp/RSPFE.tmp"
Creatingtemporaryfile"C:/DOCUME~1/Li/LOCALS~1/Temp/RSPFF.tmp"withcontents
[
kernel32.libuser32.libgdi32.libwinspool.libcomdlg32.libadvapi32.libshell32.libole32.liboleaut32.libuuid.libodbc32.libodbccp32.lib/nologo/subsystem:windows/incremental:yes/pdb:"Debug/WinMain.pdb"/debug/machine:I386/out:"Debug/WinMain.exe"
/pdbtype:sept
"./Debug/WinMain.obj"
]
Creatingcommandline"link.exe@C:/DOCUME~1/Li/LOCALS~1/Temp/RSPFF.tmp"
OutputWindow
Compiling...
WinMain.cpp
Linking...
Results
WinMain.exe-0error(s),0warning(s)
编写程序后小结:
第一个小结:
caseWM_CLOSE:
if(IDYES==MessageBox(hwnd,"是否真的退出?","询问窗口",MB_YESNO))
{
DestroyWindow(hwnd);
}
break;
caseWM_DESTROY:
PostQuitMessage(0);
break;
可以直接关闭而不弹窗修改为:
caseWM_CLOSE:
DestroyWindow(hwnd);
break;
caseWM_DESTROY:
PostQuitMessage(0);
break;
第二个小结:
caseWM_PAINT:
HDChDC;
PAINTSTRUCTps;
hDC=BeginPaint(hwnd,&ps);
TextOut(hDC,0,0,"重刷的时候不会被去掉的文字",strlen("重刷的时候不会被去掉的文字"));
TextOut(hDC,0,25,szChar,strlen(szChar));
EndPaint(hwnd,&ps);
break;
TextOut可以用多次,多次使用时只要不是文字的覆盖那么每次TextOut的执行不会清除上一次的结果。
第三个小结:
变量可以设置为全局的但是程序不能设置为全局的,如:
#include<windows.h>
#include<stdio.h>
charszChar[26];
……
第四个小结:
函数调用时,被调用的函数需要提前做定义,同时使用的时候要和定义的函数名称一致,否则程序无法执行。
第五个小结:
当程序修改后调试的时候必须将以前调试运行的程序关闭,否则会出现错误:
--------------------Configuration:WinMain-Win32Debug--------------------
Linking...
LINK:fatalerrorLNK1168:cannotopenDebug/WinMain.exeforwriting
Errorexecutinglink.exe.
WinMain.exe-1error(s),0warning(s)
第六个小结:
/0,/netc.在sprintf();中不起任何作用。
第七个小结:
Sprintf中可以有多个参数的变量,如同时有%d、%cetc.
sprintf(szChar,"您所输入的值为:/n%d,%c",wParam,wParam);
遇到一个问题:
当关掉编译器后双击*.cpp这个文件的时候会打开编译器,但是编译运行的时候系统就会报错!!!
自己修改的程序:
#include<windows.h>
#include<stdio.h>
charszChar[26];
LRESULTCALLBACKWinSunProc(//可以通过MSDN查找,不过函数的名称要根据自己的需要修改。
HWNDhwnd,//handletowindow
UINTuMsg,//messageidentifier
WPARAMwParam,//firstmessageparameter
LPARAMlParam//secondmessageparameter
);//函数原型声明
intWINAPIWinMain(
HINSTANCEhInstance,//handletocurrentinstance
HINSTANCEhPrevInstance,//handletopreviousinstance
LPSTRlpCmdLine,//commandline
intnCmdShow//showstate
)
{
sprintf(szChar,"你好");//要想对szChar屏幕输出的时候无乱码,sprintf要放在主函数里对scChar定义。
WNDCLASSwndcls;
wndcls.cbClsExtra=0;
wndcls.cbWndExtra=0;
wndcls.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
wndcls.hCursor=LoadCursor(NULL,IDC_CROSS);
wndcls.hIcon=LoadIcon(NULL,IDI_QUESTION);
wndcls.hInstance=hInstance;
wndcls.lpfnWndProc=WinSunProc;
wndcls.lpszClassName="liqiang2009";
wndcls.lpszMenuName=NULL;
wndcls.style=CS_HREDRAW|CS_VREDRAW;
RegisterClass(&wndcls);
HWNDhwnd;
hwnd=CreateWindow("liqiang2009","一个简单的窗口",WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,
NULL,NULL,hInstance,NULL);
ShowWindow(hwnd,SW_SHOWNORMAL);
UpdateWindow(hwnd);
MSGmsg;
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return0;
}
LRESULTCALLBACKWinSunProc(
HWNDhwnd,//handletowindow
UINTuMsg,//messageidentifier
WPARAMwParam,//firstmessageparameter
LPARAMlParam//secondmessageparameter
)
{
//charszChar[22];
//sprintf(szChar,"你好");
switch(uMsg)
{
caseWM_CHAR:
HDChdcc;
hdcc=GetDC(hwnd);
sprintf(szChar,"您所输入的值为:/n%d,%c",wParam,wParam);
TextOut(hdcc,0,75,szChar,strlen(szChar));
MessageBox(hwnd,szChar,"按键值",0);
ReleaseDC(hwnd,hdcc);
break;
caseWM_LBUTTONDOWN:
MessageBox(hwnd,"您按下了鼠标的左键","按键值",0);
HDChdc;
hdc=GetDC(hwnd);
TextOut(hdc,0,50,"HDChdc",strlen("HDChdc"));
ReleaseDC(hwnd,hdc);
break;
caseWM_PAINT:
HDChDC;
PAINTSTRUCTps;
hDC=BeginPaint(hwnd,&ps);
TextOut(hDC,0,0,"重刷的时候不会被去掉的文字",strlen("重刷的时候不会被去掉的文字"));
TextOut(hDC,0,25,szChar,strlen(szChar));
EndPaint(hwnd,&ps);
break;
caseWM_CLOSE:
//if(IDYES==MessageBox(hwnd,"是否真的退出?","询问窗口",MB_YESNO))
//{
DestroyWindow(hwnd);
//}
break;
caseWM_DESTROY:
PostQuitMessage(0);
break;
default:
returnDefWindowProc(hwnd,uMsg,wParam,lParam);
}
return0;
}
MSG(message)消息结构体
操作系统将每一个事件包装成一个称为消息MSG的结构体传递给应用程序
MSG的结构定义如下:(windowsuserinterface:platformsdk)
TypedefstructtagMSG{
HWNDhwnd;窗口的句柄句柄为资源的标识,按类型分为HICON/HCURSOR/HWND/HINSTANCE
UINTmessage;无符号整形具体的消息;用宏来表示数值WM开头的为宏WM_*
WPARAMwParam;整型.关于消息的附加信息.WM_CHAR消息字符的代码ASCII;
LPARAMlParam;;整型.关于消息的附加信息.PARAM=PARAMETER参数,参量
DWORDtime;;WORD16位的整数DWORD=DoubleWORD32位整数.指定消息的投递时间.
}MSG,*PMSG;
TypedefstructtagPOINT{
LONGx;
LONGy;
}POINT,*PPOINT;结束时间2009年2月19日4:57:49
2009年2月20日13:18:04(第二次)
消息:消息本身,特定的消息响应
WINMAN函数
入口点函数类似于C的MAN函数
intWINAPIWinMan(
HINSTANCEhInstance,//handletocerrentinstance实例(资源)的句柄,当前运行的句柄;
HINSTANCEhPrevinstance,//handletopreviousinstance先前实例的句柄,可能为空,兄弟的实例句柄
win32下这个实例总是为空(win98、2000等);
LPSTRlpCndLine,//commandlineLP->LONGPOINTER长指针,ETRING字符串,指向字符
串的指针;类似于CHAR*命令行参数
intnCmdSbow//showstate显示的状态,比如窗口的大小全屏
);//这个分号在具体的编程过程中是不要的,需要删除掉。
DOS下的MAIN函数可以接收2个参数:ARJc、ARJv.ARJc用来接受、存放命令行参数的个数,ARJv指针数组用来存放命令行参数,同样的在windows程序当中他也可以接收命令行的参数.
WINAPI:Callingconventionforsystemfunctions.
2009年2月24日15:10:15(第三次)
窗口的创建。创建一个完整的窗口需要经过下面四个操作步骤:
l设计一个窗口类;
l注册窗口类;
l创建窗口;
l显示及更新窗口;
1、设计窗口。设计一个窗口就是设计一个窗口类,他是一个结构体
WNDCLASS
Typedefstruct_WNDCLASS{//MSDN--WNDCLASS
UINITstyle;style//(ClassStyles=CS)指定一个类的类型,这里是窗口类的类型;OR(|),与(&),取反(~),
赋值:wndclass.style=CS_HREDRAW|CS_VREDRAW(水平和垂直重画)
WNDPROClpfnWndProc;窗口过程。Lp=longpointer.fn=functionProc=procedure用来接收一个函数指针。
回掉函数(lpfnWndProc:Pointertothewindowprocedure.Youmustusethe
CallWindowProc[回调window进程]functiontocallthewindowprocedure.Formore
information,seeWindowProc.)。这里我们会把一个窗口名赋给他(函数名相当于一个
函数的指针),即在设计的时候就确定了一个回调函数,这里给他赋了WinSunProc
这个函数,当然WinSunProc函数具体内容是什么需要程序员自己去编写。
intcbClsExtra;windows程序会为每一个类管理一个内部数据结构。类附加内存,通常情况下设为零.(=0)
类额外的一个数据。(附加字节数)
intcbWndExtra;windows程序会为每一个窗口管理一个内部数据结构。窗口附加内存,通常情况下也设
置为零。以上两个分配额外的内存,通常我们不需要所以设置为零。(=0)
窗口类额外的一个数据。通常赋值为零表示我们不需要额外的内存,但是如果我们需要则额外的内存空间会自己变为零。这里设置为零就是=0,不同于其他的设置为NULL,因为这里是整数。
HANDLEhInstance;代表我们当前程序的实例号。操作系统分配,直接通过WinMain的形参传递过来。
代表了我们当前应用程序的实例号,我们在设计窗口类的时候需要知道他是代表那
个应用程序实例的。从bInstance获得这个程序。
HICONhIcon;图标的句柄。用LoadIcon这个函数来赋值hIcon=LoadIcon(……)
HCURSORhCursor;光标句柄。用LoadCursor这个函数来赋值hCursor=LoadCursor(……)
HBRUSHhbrBackground;画刷的句柄,用的是GetStockObject()这么一个函数
LPCTSTRlpszMenuName;LP=longpoint(32位),CT=constant常量,STR=string.用来设定菜单的名字;
可以直接设置为空NULL,如:lpszMenuNam=NULL;
(如果这个类型不认识可以用datatype这个来查看不是SDK)
LPCTSTRlpszClassName;设置一个类的名字(注册窗口的时候会用到),窗口设计完成之后需要给窗口取一
个名字。
}WNDCLASS,*PWNDCLASS;
这个函数是系统定义好了格式,我们只需要拿过来用,直接对参数进行赋值。
WindowProc回调函数,窗口过程函数
LRESULTCALLBACKWindowProc(//CALLBACK:窗口过程函数前边定义的时候加了一个CALLBACK
#defineCALLBACK_stdcall
CALLBACK就是一个类型的定义
LRESULT就是一个长整型,返回一个结果码。
HWNDhwnd,//handletowindow
UINTuMsg,//messageidentifier
WPARAMwParam,//firstmessageparameter
LPARAMlParam//secondmessageparameter
);
DefWindowProc回调函数,窗口过程函数
LRESULTDefWindowProc(
HWNDhWnd,//handletowindow
UINTMsg,//messageidentifier
WPARAMwParam,//firstmessageparameter
LPARAMlParam//secondmessageparameter
);
_stdcall和_cdecl这是两种调用的约定,_stdcall标准调用约定,_cdecl为c语言的调用约定。标准调用约定也是PASCAL调用约定,dephi就是pascal调用约定。_stdcall和_cdecl在参数的传递顺序和堆栈的清除这两个方面有差异。Vc++默认的调用约定选项是_cdecl.需要时要经行定义。也可以通过Project->ProjectSettings->C/C++->Callingconvention:中选择_stdcall这种方式来设定。
LoadIcon
HICONLoadIcon(
HINSTANCEhInstance,//handletoapplicationinstance应用程序实例的一个句柄。当标准的图标加载的
时候这个参数用空(NULL),
LPCTSTRlpIconName//namestringorresourceidentifier当第一个参数取值为NULL时第二个参数可以选择
);
如:LoadIcon(NULL,IDI_ERROR)//ID=identifier
LoadCursor
HCURSORLoadCursor(
HINSTANCEhInstance,//handletoapplicationinstance应用程序实例的一个句柄,如果想用一个标准的光标
这个参数需要设置为NULL
LPCTSTRlpCursorName//nameorresourceidentifier同样当前一个参数设置为NULL是这个可以选择微
软提供的光标函数。
);
GetStockObject()获取一个笔、画刷、调色板、的句柄。
HGDIOBJGetStockObject(
intfnObject//stockobjecttype
);
*=(HBRUSH)GetStockObject(WHITE_BRUSH);//(HBRUSH)这里是一个强制转换。因为GetStockObject返回的是一个GetStockObject的类型,而hbrBackground定义的为HBRUSH的类型,C++语言是一个强类型语言,对函数的类型要求的很严。强制类型转换必须要有可比性,即两个类型可以转换,这种转换在编译的时候不会报错但是在执行的时候会出现错误。
2、注册。窗口设置完之后需要对其进行注册(向操作系统注册),RegisterClass()窗口的注册函数
RegisterClass
ATOMRegisterClass(
CONSTWNDCLASS*lpWndClass//classdata窗口类结构体的指针直接用定义的窗口类结构体的变量加上取
地址符&
);
3、创建一个窗口。
首先需要定义一个句柄(HWND),利用这个句柄的变量保存我们新创建的窗口的标识。然后利用CreateWindow函数来进行窗口的创建。
HWNDhwnd;
Hwnd=CreateWindow(……);
CreateWindow
HWNDCreateWindow(
LPCTSTRlpClassName,//registeredclassname注册的类名,设计窗口类的时候指定的名字,如果名称与开始
定义的不一样那么WinMain程序会运行但是窗口是不回产生的。创建窗口的时候一
定要基于一个已经注册之后的窗口类名来设定
LPCTSTRlpWindowName,//windowname窗口的名字,即我们产生这个窗口的时候他的标题。在标题栏上显
示的窗口的名字,标题栏是一条蓝色的在窗口顶端的长条
DWORDdwStyle,//windowstyle窗口的类型,要和刚才的类的类型区分开来。(WS)如:
WS_OVERLAPPEDWINDOW
intx,//horizontalpositionofwindow窗口的水平坐标;CW_USEDEFAULT,如果x设置为
CW_USEDEFAULT(系统缺省坐标)那么y坐标值就会被忽略。另外屏幕坐标左上角为
原点(0,0)而数学中的为左下角是原点
inty,//verticalpositionofwindow窗口的垂直坐标;x,y显示的时候窗口的左上角的x,y坐标.
intnWidth,//windowwidth窗口的宽度,CW_USEDEFAULT用的是系统缺省的宽度和高度。同样
如果用CW_USEDEFAULT则nHeight会被忽略。
intnHeight,//windowheight窗口的高度
HWNDhWndParent,//handletoparentorownerwindow窗口的句柄,这里指的是父窗口的句柄,如果程
序只有1个窗口没有父窗口则可以设置为空(NULL)
HMENUhMenu,//menuhandleorchildidentifier菜单的句柄。如果用不倒菜单那么我们可以把菜单句柄
设置为空(NULL);
HINSTANCEhInstance,//handletoapplicationinstance当前应用程序实例的句柄,通过WinMain函数传递过
来,直接把实例号hInstance,复制过来就可以了
LPVOIDlpParam//window-creationdata当窗口创建的时候都会有WM_CREATE这个消息产生,
作为WM_CREATE的附加消息有两个由lParam的消息内容传进来。指向
CREATESTRUCT结构体,该结构题的lParam参数是通过创建窗口时产生的
WM_CREATE消息时产生的附加消息中的lParam参数传递过来的,即:作为
WM_CREATE附加消息lParam传递过来的参数数据的指针。如果一个应有实例
CreateWindow创建一个多文档multipledocumentinterface(MDI)界面的窗口该函数
lpParam指针指向结构体CLIENTCREATESTRUCT。
);
WS_OVERLAPPEDWINDOW
WS_OVERLAPPEDWINDOW(
WS_OVERLAPPED|//标识产生一个层叠的窗口,层叠的窗口就是有一个标题栏还有一个边框
WS_CAPTION|//创建一个有标题栏的窗口
WS_SYSMENU|//创建一个带有系统菜单的窗口
WS_THICKFRAME|//创建一个具有可调边框的窗口
WS_MINIMIZEBOX|//创建一个具有最小化按钮的窗口
WS_MAXIMIZEBOX//创建一个具有最大化按钮的窗口
).SameastheWS_TILEDWINDOWstyle.
上边的特征都是有二进制位表示有无。
CREATESTRUCT
typedefstructtagCREATESTRUCT{
LPVOIDlpCreateParams;
HINSTANCEhInstance;
HMENUhMenu;
HWNDhwndParent;
intcy;
intcx;
inty;
intx;
LONGstyle;
LPCTSTRlpszName;
LPCTSTRlpszClass;
DWORDdwExStyle;
}CREATESTRUCT,*LPCREATESTRUCT;
WM_CREATE:WindowProc
LRESULTCALLBACKWindowProc(
HWNDhwnd,//handletowindow
UINTuMsg,//WM_CREATE
WPARAMwParam,//notused
LPARAMlParam//creationdata(LPCREATESTRUCT)
);
4、显示、更新窗口
窗口创建完之后需要显示出来。显示窗口调用函数为:ShowWindow
ShowWindow指定窗口的显示状态
BOOLShowWindow(
HWNDhWnd,//handletowindow窗口的句柄,即要显示的是哪一个窗口,把窗口的标识赋给他
intnCmdShow//showstate窗口的显示状态。如:最大化显示,最小化显示等。(SHOW_SHOWNORMAL
正常化显示)
);
UpdateWindow
显示完窗口之后我们还需要调用一个函数,即为更新窗口的函数,UpdateWindow。当然这个函数如果不添加也不影响窗口的产生。
BOOLUpdateWindow(
HWNDhWnd//handletowindow窗口句柄。
);
消息循环,这里最重要的环节。
首先用MSG结构体定义了一个消息结构体的变量。用While循环对GetMessage进行循环。GetMessage调用消息队列中的消息。
MSGmsg;
While(GetMessage(&msg,NULL,0,0))//当GetMessage取到的消息不是WM_QUIT时执行While循环。
{
TranslateMessage(&msg);//转换消息,翻译消息。对取到的消息对进行转换。当我们按下某一个按键
的时候系统将会产生一个WM_KEYDOWN和WM_KEYUP这样两个的消
息,并提供一个按键的虚拟的扫描码。TranslateMessage将WM_KEYDOWN
和WM_KEYUP消息对转换成ASCII码WM_CHAR消息,并将转换后的消息
投递到消息队列中,转换过程中不会影响原来的消息只会产生一个新的消
息。如果不用这个函数那么我们不可能收到WM_CHAR消息的。
DispatchMessage(&msg);//将收到的消息(TranslateMessage(&msg)转换的消息)传递到窗口的回调函数
即窗口的过程函数中处理,即将消息路由给了操作系统,然后操作系统去调
用窗口过程函数,即我们在设计窗口类WNDCLASS的时候设计的窗口过程
函数WNDPROClpfnWndProc所设定的回调函数进行处理。
}
GetMessage
BOOLGetMessage(//如果返回值是WM_QUIT则返回一个零值,如果返回的不是WM_QUIT则返回的是一
个非零值。
LPMSGlpMsg,//messageinformation[out]PointertoanMSGstructurethatreceivesmessage
informationfromthethread's(线程)messagequeue.消
息结构体的指针。
HWNDhWnd,//handletowindow句柄表示想要获取那个窗口的消息,如果设置为NULL,标识
我们要获取属于这个调用线程的任何窗口的消息
UINTwMsgFilterMin,//firstmessage指定消息的最小的消息值
UINTwMsgFilterMax//lastmessage指定一个消息的最大的消息值,即最后一个消息(MSDN有一
个错误他将last写成first)如果这两个都设置为零那么
GetMessage就没有对消息进行过滤而是针对所有的消息,没有
范围的过滤。
);
[out]->表明我在对lpMsg传参的时候不要对内部成员经行初始化,只需要定义一个结构体的变量将他的地址放在哪里,通过函数的调用他会自动的填充消息结构体内部的成员变量。
GetMessag返回的是一个BOOL型的值。当从消息队列中取回消息是返回值为真,当消息队列中始终都有的消息的时候则返回的值始终都是真。
TranslateMessage
BOOLTranslateMessage(
CONSTMSG*lpMsg//messageinformation
);
DispatchMessage
LRESULTDispatchMessage(
CONSTMSG*lpmsg//messageinformation
);
窗口过程函数(WNDPROClpfnWndProc指定的函数),即回调函数里边的代码
这里设定的窗口调用函数的名称为WinSunProc.怎么知道WinSunProc是什么样的格式呢?选择查看的方式如下:
WNDCLASS->PlatformSDK:WindowsUserInterface->选择seeWindowProc中的WindowProc->我们可以看到函数的形式规定的形式(函数名称可以更改,但是参数的类型不能更改但参数的名字可以更改):
LRESULTCALLBACKWindowProc(//LRESULT就是一个LONG型CALLBACK后边将介绍
HWNDhwnd,//handletowindow窗口的句柄
UINTuMsg,//messageidentifier消息的标识
WPARAMwParam,//firstmessageparameter消息的附加参数
LPARAMlParam//secondmessageparameter消息的第二个附加参数
);
实际上是将消息结构体的前四个参数作为参数传递给WindowProc(这个名称用户可以自己更改)
sprintf:printf在c语言中是想屏幕中输出内容,sprintf是c语言中的一个库函数,是格式化一个文本到内存buffer中,即一个内存区中.指定的区域中打印一些数据的。如下:
charszChar[20];
sprintf(szChar,”charis%d”,wParam);//将wParam的ASCII码的消息以%d的格式格式化到szChar这个字符数组
当中。
MessageBox(hwnd,szChar,”weixin”,MB_ok);//MessageBox用来弹出一个消息框.
MessageBox
intMessageBox(
HWNDhWnd,//handletoownerwindow消息框被创建那个窗口拥有这个消息框的句柄
LPCTSTRlpText,//textinmessagebox消息显示的文本
LPCTSTRlpCaption,//messageboxtitle消息的标题,即在消息的蓝框中的内容显示
UINTuType//messageboxstyle消息框的类型,如MB_OK表示消息框包含了一个OK按键
的button按钮。MB_OK定义的是”0”,我们可以直接用”0”代
替MB_OK.
);
文字的输出
HDChDC;//HDC是一个句柄,DC(devicecontext设备上下文)的句柄。DC是系统内部维护的数据结构占
内存。
hDC=GetDC(hwnd);//获取函数hDC的句柄
TextOut(hDC,0,50,”计算机编程语言培训”,strlen(“计算机编程语言培训”));//文本输出的函数,在窗口上输出一个
文本。
ReleaseDC(hwnd,hDC);//释放DC.如果我们使用了一个DC而没有在使用完之后释放DC那么会造成内存的泄
露。GetDC和ReleaseDC是一对。
HDC:
Handletoadevicecontext(DC).
GetDC
HDCGetDC(
HWNDhWnd//handletowindow窗口的句柄。跟那个窗口相关的DC,利用DC画图的时候就画在那个窗口
上。
);
TextOut
BOOLTextOut(
HDChdc,//handletoDCDC的句柄
intnXStart,//x-coordinateofstartingposition开始x坐标位置
intnYStart,//y-coordinateofstartingposition开始y坐标位置
LPCTSTRlpString,//characterstring输出文本内容
intcbString//numberofcharacters输出文本字符数量。用c的strlen.
);
WM_PAINT当窗口重绘的时候就会发送这个消息。当窗口从无到有的时候会使用这个函数,同时在我们在调试运行时设置的断点中不能在WM_PAINT中设置,如果这样做了那么窗口将会永远不会产生。前面说过当我们窗口的水平或者垂直坐标发生改变的时候窗口都会重绘,重绘就是通过发送WM_PAINT这个消息来完成的。
CaseWM_PAINT://窗口的绘画,下边为响应消息的设置
hdc=BeginPaint(hwnd,&ps);//获取DC的句柄,这个DC是在选择语句上边预定的。
TextOut(hdc,0,0,”北京维新科学技术培训中心”,strlen(”北京维新科学技术培训中心”));//
EndPaint(hwnd,&ps);//释放DC.BeginPaint和EndPaint是一对,并且只能在WM_PAINT上使用。
break;
BeginPaint
HDCBeginPaint(
HWNDhwnd,//handletowindow窗口的句柄
LPPAINTSTRUCTlpPaint//paintinformation设置PAINTSTRUCT结构体的指针。
);
BeginPaint准备为指定的窗口进行绘画,BeginPaint函数自动填充PAINTSTRUCT结构体,PAINTSTRUCT结构体不需要我们来维护由系统内部lpPaint来维护
PAINTSTRUCT
typedefstructtagPAINTSTRUCT{
HDChdc;
BOOLfErase;
RECTrcPaint;
BOOLfRestore;
BOOLfIncUpdate;
BYTErgbReserved[32];
}PAINTSTRUCT,*PPAINTSTRUCT;
WM_CLOSE
caseWM_CLOSE://当我们点击关闭按钮的时候会产生这样的消息。
if(IDYES==MessageBox(hwnd,”你是否要退出程序?”,”weixin”,MB_YESNO))//由MessageBox返回一个ID*
的参数,判断该返回参数与IDYES的比较情况。
{
DestroyWindow(hwnd);//销毁窗口
}
break;
IDYES是一个常量,我们在程序设计的是有有一个小经验:在if语句判断的时候一般把常量放在前边。这样做方便我们找到错误。如:if(x=1)和if(1=x),这两种是在我们输入的时候容易出错的两种情况,第二种在编译的时候就会报错,而第一种x=1是“真”,编译的时候不会报错。
DestroyWindow
BOOLDestroyWindow(
HWNDhWnd//handletowindowtodestroy//要被销毁的窗口的句柄。
);
DestroyWindow这个函数会发送WM_DESTROY和WM_NCDESTROY这两个消息到消息队列中。
WM_DESTROY
CaseWM_DESTROY
PostQuitMessage(0);
Break;
小经验:
caseWM_CLOSE:
if(IDYES==MessageBox(hwnd,”你是否要退出程序?”,”weixin”,MB_YESNO))
{
DestroyWindow(hwnd);
}
break;
CaseWM_DESTROY
PostQuitMessage(0);
Break;
这个程序片段的意思是当MessageBox弹出后用户选择YES时,if语句为真,执行DestroyWindow(hwnd)关掉窗口,同时产生WM_SESTROY和WM_NCDESTROY两个消息,在caseWM_DESTROY中将关掉程序执行的进程,同时程序结束。
caseWM_CLOSE:
DestroyWindow(hwnd);
break;
CaseWM_DESTROY
if(IDYES==MessageBox(hwnd,”你是否要退出程序?”,”weixin”,MB_YESNO))
{
PostQuitMessage(0);
}
Break;
这个程序片段与上边的略有不同,但是执行结果则是完全不一样的。当执行WM_CLOSE时程序会首先执行DestroyWindow(hwnd)关掉窗口产生WM_SESTROY和WM_NCDESTROY两个消息到消息对立中,这个时候响应消息WM_DESTROY,开始执行if的判断,此时才弹出MessageBox的消息对话框,如果用户选择的是否,那么消息对话框关闭了,if语句为否不执行其中的PostQuitMessage(0)也就无法产生WM_QUIT消息,此时窗口关闭了,但是程序没有终止而是在后台中运行。
PostQuitMessage
VOIDPostQuitMessage(//产生WM_QUIT这个消息
intnExitCode//exitcode
);
default
在写消息循环的时候default语句是必不可少的,是对缺省的没有定义的消息由系统进行默认的响应,如果没有那么那些没有定义的消息将会缺少归宿,并且窗口可能不能正常的显示,但是程序还是在后台运行。
deault:
returnDefWindowProc(hwnd,uMsg,wParam,lParem);
return0;
函数的正常返回值,返回一个”0”值。
编写一个程序:
新建一个project:File->New…->Projects->Win32Application|Location:存放地址|Project
name:工程名字->OK->step1:Anemptproject->finish->ok.
新建:File->New…->Files->c++sourcefile(c++源文件)|file:同样的名字->ok.
既然我们编写的是windows程序,所以就要包含#include<windows.h>windows头文件,还有一个就是c语言的头文件#include<stdio.h>原因是我们要用到c语言的库函数。
--------------------------------------------------------------------------------------------------------------------------------------
#include<windows.h>
#include<stdio.h>
LRESULTCALLBACKWinSunProc(//可以通过MSDN查找,不过函数的名称要根据自己的需要修改。
HWNDhwnd,//handletowindow
UINTuMsg,//messageidentifier
WPARAMwParam,//firstmessageparameter
LPARAMlParam//secondmessageparameter
);//函数原型声明
intWINAPIWinMain(
HINSTANCEhInstance,//handletocurrentinstance
HINSTANCEhPrevInstance,//handletopreviousinstance
LPSTRlpCmdLine,//commandline
intnCmdShow//showstate
)
{
WNDCLASSwndcls;
wndcls.cbClsExtra=0;
wndcls.cbWndExtra=0;
wndcls.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
wndcls.hCursor=LoadCursor(NULL,IDC_CROSS);
wndcls.hIcon=LoadIcon(NULL,IDI_QUESTION);
wndcls.hInstance=hInstance;
wndcls.lpfnWndProc=WinSunProc;
wndcls.lpszClassName="liqiang2009";
wndcls.lpszMenuName=NULL;
wndcls.style=CS_HREDRAW|CS_VREDRAW;
RegisterClass(&wndcls);
HWNDhwnd;
hwnd=CreateWindow("liqiang2009","一个简单的窗口",WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,
NULL,NULL,hInstance,NULL);
ShowWindow(hwnd,SW_SHOWNORMAL);
UpdateWindow(hwnd);
MSGmsg;
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return0;
}
LRESULTCALLBACKWinSunProc(
HWNDhwnd,//handletowindow
UINTuMsg,//messageidentifier
WPARAMwParam,//firstmessageparameter
LPARAMlParam//secondmessageparameter
)
{
switch(uMsg)
{
caseWM_CHAR:
charszChar[20];
sprintf(szChar,"您所输入的按键值为:%d或%c",wParam,wParam);
MessageBox(hwnd,szChar,"按键值",0);
break;
caseWM_LBUTTONDOWN:
MessageBox(hwnd,"您按下了鼠标的左键","按键值",0);
HDChdc;
hdc=GetDC(hwnd);
TextOut(hdc,0,25,"HDChdc",strlen("HDChdc"));
ReleaseDC(hwnd,hdc);
break;
caseWM_PAINT:
HDChDC;
PAINTSTRUCTps;
hDC=BeginPaint(hwnd,&ps);
TextOut(hDC,0,0,"重刷的时候不会被去掉的文字",strlen("重刷的时候不会被去掉的文字"));
EndPaint(hwnd,&ps);
break;
caseWM_CLOSE:
if(IDYES==MessageBox(hwnd,"是否真的退出?","询问窗口",MB_YESNO))
{
DestroyWindow(hwnd);
}
break;
caseWM_DESTROY:
PostQuitMessage(0);
break;
default:
returnDefWindowProc(hwnd,uMsg,wParam,lParam);
}
return0;
}
------------------------------------------------------------------------------------------------------------------------------------------
BuildLog
--------------------Configuration:WinMain-Win32Debug--------------------
CommandLines
Creatingtemporaryfile"C:/DOCUME~1/Li/LOCALS~1/Temp/RSPFE.tmp"withcontents
[
/nologo/MLd/W3/Gm/GX/ZI/Od/D"WIN32"/D"_DEBUG"/D"_WINDOWS"/D"_MBCS"/Fp"Debug/WinMain.pch"/YX/Fo"Debug/"/Fd"Debug/"/FD/GZ/c
"D:/soft-教程/vc++/孙鑫课件/lessons/lesson1/WinMain/WinMain.cpp"
]
Creatingcommandline"cl.exe@C:/DOCUME~1/Li/LOCALS~1/Temp/RSPFE.tmp"
Creatingtemporaryfile"C:/DOCUME~1/Li/LOCALS~1/Temp/RSPFF.tmp"withcontents
[
kernel32.libuser32.libgdi32.libwinspool.libcomdlg32.libadvapi32.libshell32.libole32.liboleaut32.libuuid.libodbc32.libodbccp32.lib/nologo/subsystem:windows/incremental:yes/pdb:"Debug/WinMain.pdb"/debug/machine:I386/out:"Debug/WinMain.exe"
/pdbtype:sept
"./Debug/WinMain.obj"
]
Creatingcommandline"link.exe@C:/DOCUME~1/Li/LOCALS~1/Temp/RSPFF.tmp"
OutputWindow
Compiling...
WinMain.cpp
Linking...
Results
WinMain.exe-0error(s),0warning(s)
编写程序后小结:
第一个小结:
caseWM_CLOSE:
if(IDYES==MessageBox(hwnd,"是否真的退出?","询问窗口",MB_YESNO))
{
DestroyWindow(hwnd);
}
break;
caseWM_DESTROY:
PostQuitMessage(0);
break;
可以直接关闭而不弹窗修改为:
caseWM_CLOSE:
DestroyWindow(hwnd);
break;
caseWM_DESTROY:
PostQuitMessage(0);
break;
第二个小结:
caseWM_PAINT:
HDChDC;
PAINTSTRUCTps;
hDC=BeginPaint(hwnd,&ps);
TextOut(hDC,0,0,"重刷的时候不会被去掉的文字",strlen("重刷的时候不会被去掉的文字"));
TextOut(hDC,0,25,szChar,strlen(szChar));
EndPaint(hwnd,&ps);
break;
TextOut可以用多次,多次使用时只要不是文字的覆盖那么每次TextOut的执行不会清除上一次的结果。
第三个小结:
变量可以设置为全局的但是程序不能设置为全局的,如:
#include<windows.h>
#include<stdio.h>
charszChar[26];
……
第四个小结:
函数调用时,被调用的函数需要提前做定义,同时使用的时候要和定义的函数名称一致,否则程序无法执行。
第五个小结:
当程序修改后调试的时候必须将以前调试运行的程序关闭,否则会出现错误:
--------------------Configuration:WinMain-Win32Debug--------------------
Linking...
LINK:fatalerrorLNK1168:cannotopenDebug/WinMain.exeforwriting
Errorexecutinglink.exe.
WinMain.exe-1error(s),0warning(s)
第六个小结:
/0,/netc.在sprintf();中不起任何作用。
第七个小结:
Sprintf中可以有多个参数的变量,如同时有%d、%cetc.
sprintf(szChar,"您所输入的值为:/n%d,%c",wParam,wParam);
遇到一个问题:
当关掉编译器后双击*.cpp这个文件的时候会打开编译器,但是编译运行的时候系统就会报错!!!
BuildLog
--------------------Configuration:WinMain-Win32Debug--------------------
CommandLines
Creatingtemporaryfile"C:/DOCUME~1/Li/LOCALS~1/Temp/RSP13C.tmp"withcontents
[
kernel32.libuser32.libgdi32.libwinspool.libcomdlg32.libadvapi32.libshell32.libole32.liboleaut32.libuuid.libodbc32.libodbccp32.lib/nologo/subsystem:console/incremental:yes/pdb:"Debug/WinMain.pdb"/debug/machine:I386/out:"Debug/WinMain.exe"/pdbtype:sept
"./Debug/WinMain.obj"
]
Creatingcommandline"link.exe@C:/DOCUME~1/Li/LOCALS~1/Temp/RSP13C.tmp"
OutputWindow
Linking...
LIBCD.lib(crt0.obj):errorLNK2001:unresolvedexternalsymbol_main
Debug/WinMain.exe:fatalerrorLNK1120:1unresolvedexternals
Errorexecutinglink.exe.
Results
WinMain.exe-2error(s),0warning(s)
自己修改的程序:
#include<windows.h>
#include<stdio.h>
charszChar[26];
LRESULTCALLBACKWinSunProc(//可以通过MSDN查找,不过函数的名称要根据自己的需要修改。
HWNDhwnd,//handletowindow
UINTuMsg,//messageidentifier
WPARAMwParam,//firstmessageparameter
LPARAMlParam//secondmessageparameter
);//函数原型声明
intWINAPIWinMain(
HINSTANCEhInstance,//handletocurrentinstance
HINSTANCEhPrevInstance,//handletopreviousinstance
LPSTRlpCmdLine,//commandline
intnCmdShow//showstate
)
{
sprintf(szChar,"你好");//要想对szChar屏幕输出的时候无乱码,sprintf要放在主函数里对scChar定义。
WNDCLASSwndcls;
wndcls.cbClsExtra=0;
wndcls.cbWndExtra=0;
wndcls.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
wndcls.hCursor=LoadCursor(NULL,IDC_CROSS);
wndcls.hIcon=LoadIcon(NULL,IDI_QUESTION);
wndcls.hInstance=hInstance;
wndcls.lpfnWndProc=WinSunProc;
wndcls.lpszClassName="liqiang2009";
wndcls.lpszMenuName=NULL;
wndcls.style=CS_HREDRAW|CS_VREDRAW;
RegisterClass(&wndcls);
HWNDhwnd;
hwnd=CreateWindow("liqiang2009","一个简单的窗口",WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,
NULL,NULL,hInstance,NULL);
ShowWindow(hwnd,SW_SHOWNORMAL);
UpdateWindow(hwnd);
MSGmsg;
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return0;
}
LRESULTCALLBACKWinSunProc(
HWNDhwnd,//handletowindow
UINTuMsg,//messageidentifier
WPARAMwParam,//firstmessageparameter
LPARAMlParam//secondmessageparameter
)
{
//charszChar[22];
//sprintf(szChar,"你好");
switch(uMsg)
{
caseWM_CHAR:
HDChdcc;
hdcc=GetDC(hwnd);
sprintf(szChar,"您所输入的值为:/n%d,%c",wParam,wParam);
TextOut(hdcc,0,75,szChar,strlen(szChar));
MessageBox(hwnd,szChar,"按键值",0);
ReleaseDC(hwnd,hdcc);
break;
caseWM_LBUTTONDOWN:
MessageBox(hwnd,"您按下了鼠标的左键","按键值",0);
HDChdc;
hdc=GetDC(hwnd);
TextOut(hdc,0,50,"HDChdc",strlen("HDChdc"));
ReleaseDC(hwnd,hdc);
break;
caseWM_PAINT:
HDChDC;
PAINTSTRUCTps;
hDC=BeginPaint(hwnd,&ps);
TextOut(hDC,0,0,"重刷的时候不会被去掉的文字",strlen("重刷的时候不会被去掉的文字"));
TextOut(hDC,0,25,szChar,strlen(szChar));
EndPaint(hwnd,&ps);
break;
caseWM_CLOSE:
//if(IDYES==MessageBox(hwnd,"是否真的退出?","询问窗口",MB_YESNO))
//{
DestroyWindow(hwnd);
//}
break;
caseWM_DESTROY:
PostQuitMessage(0);
break;
default:
returnDefWindowProc(hwnd,uMsg,wParam,lParam);
}
return0;
}
相关文章推荐
- C++ 学习之旅 Windows程序内部运行原理
- Windows 程序内部运行原理
- Lesson1 Windows程序内部运行原理 ---孙鑫VC++教程
- MFC基础:Windows内部程序运行原理
- 【MFC】1.Windows程序内部运行原理
- 《笔记》孙鑫老师MFC第一讲(windows程序内部运行原理)
- windows程序内部运行原理
- Windows程序内部运行原理
- 孙鑫VC视频学习笔记之windows程序内部运行原理
- VC++学习(1):Windows程序内部运行原理
- VC++编程之第一课笔记――Windows程序内部运行原理
- Lesson1 Windows程序内部运行原理 ---孙鑫VC++教程
- Windows程序内部运行原理
- WinDows程序内部运行原理
- Lesson1 Windows程序内部运行原理
- 孙鑫VC学习笔记:第一讲 Windows程序内部运行原理
- MFC视频教程(孙鑫)学习笔记1-Windows程序内部运行原理
- Windows程序内部运行原理 - 孙鑫 - VC++6.0 - Code by SunXin/Remark by HackerJLY
- 孙鑫VC讲座笔记--WINDOWS程序内部运行原理
- 孙鑫VC学习(第1课--Windows程序内部运行原理)