创建一个窗体程序

#include<windows.h>
LRESULT CALLBACK WinProc(HWND hInst,UINT message,WPARAM wParam,LPARAM lParam);
//hWnd:标识调用回调函数的窗体句柄;message:标识hWnd窗体要处理的消息;wParam:一个32位的消息参数,其含义和数值根据消息的不同而不同;lParam:一个32位的消息参数,其值与消息有关
//程序代码中通常不直接调用窗体回调函数,一般由Windows系统自动调用,也可以通过SendMessage函数在程序代码中直接触发窗体回调函数的执行
//LRESULT CALLBACK它们其实是宏,表示函数的返回类型,你可以在VC里面对它们点右键然后Go To Definition看到它的宏定义。其实,这就表示了WinProg函数是一个回调函数。
//在MFC中,得到Message消息以后系统会进行回调,当然,我们需要编写一个回调函数来响应。为了区别于其它函数,在回调函数前加上LRESULT CALLBACK,这就是下面那个和这个类似的原因

INT WINAPI WinMain(HINSTANCE hInst,HINSTANCE hPrevInst,LPSTR lpCmdLine,INT nShowCmd)          //参数:实例句柄、前一个实例的句柄、命令行参数、窗口显示方式
{
WNDCLASSEX wClass;                                   //用WNDCLASSEX声明一个窗体类wClass

ZeroMemory(&wClass,sizeof(WNDCLASSEX));               //结构体清零,不让结构的成员数值具有不确定性

wClass.cbClsExtra=NULL;                                             //cbClsExtra:指定紧跟在窗体类结构后的附加字节数

wClass.cbSize=sizeof(WNDCLASSEX);                                      //cbSize:WNDCLASSEX的大小,可以用sizeof(WNDCLASSEX)来获取准确的值

wClass.cbWndExtra=NULL;                                                //cbWndExtra:指定紧跟在窗体实例后的附加字节数
//系统将其初始化为零。如果应用程序正在用WNDCLASS结构注册一个在RC

wClass.hbrBackground=(HBRUSH)COLOR_WINDOW;                           //hbrBackground:窗体的背景颜色
//关于wndclass.hbrBackground=(HBRUSH)GetStockObject(BLACK_BRUSH);
//GetStockObject返回HGDIOBJ类型,这个是GDI绘图时需要的类的基类,下面派生有PEN,BITMAP,BRUSH等等, HBRUSH简单的说就是笔刷,用来表描述画图是使用的笔刷类型,因为wndclass.hbrBackground是HBUSH类型所有需要转化

wClass.hCursor=LoadCursor(NULL,IDC_ARROW);                           //hCursor:光标的句柄,第二个参数可以参考书本里面,其中IDC_ARROW是一个箭头的图标
//标识该窗口类的光标,hCursor必须是一个光标资源的句柄。若hCursor字段为NULL,则无论何时鼠标移到应用程序窗口时,应用程序必须显式设置光标形状。
//LoadCursor,是一个函数功能,该函数从一个与应用事例相关的可执行文件(EXE文件)中载入指定的光标资源。

wClass.hIcon=NULL;                                                 //hIcon:图标的句柄
//标识了该窗口类的图标。hIcon字段必须是一个图标的句柄;若hIcon字段为NULL,那么系统将提供一个默认的图标。

wClass.hIconSm=NULL;                                               //hIconSm:窗体类关联的小图标。如果该值为NULL,则把hIcon中的图标转换成大小合适的小图标

wClass.hInstance=hInst;                                              //hInstance:标识了该窗口类的窗口过程所在的模块实例的句柄,不能为NULL。

wClass.lpfnWndProc=(WNDPROC)WinProc;                                //lpfnWndProc:窗体消息处理函数(回调函数)指针
//每个窗口会有一个称为窗口过程的回调函数(WndProc),它带有四个参数,分别为:窗口句柄(Window Handle) HWND  ,  消息ID(Message ID) UINT  ,  和两个消息参数(wParam, lParam)WPARAM、LPARAM,
//WndProc的第一个参数hWnd就是当前接收消息的窗口句柄,第二个参数就是被传送过来的消息,第三、第四个参数都是附加在消息上的数据,这和MSG结构体是一样的。

wClass.lpszClassName="Window Class";                                //lpszClassName:指向类名称的指针
//指向NULL结束的字符串,或者是一个原型(atom)。若该参数是一个原型,它必须是一个有先前调用RegisterClass或者 RegisterClassEx函数产生的类原型。
//类原型必须作为lpszClassName的低字,高字必须为0.若lpszClassName是一个字符串,它描述了窗口类名。这个类名可以是由RegisterClass或RegisterClassEx注册的名字,或者是任何预定义的控件类名

wClass.lpszMenuName=NULL;                                           //lpszMenuName:指向菜单的指针
//指向NULL结束的字符串,该字符串描述菜单的资源名,如同在资源文件里显示的名字一样。若使用一个整数标识菜单,可以使用MAKEINTRESOURCE宏。如果lpszMenuName为NULL,那么该窗口类的窗口将没有默认菜单。

wClass.style=CS_HREDRAW|CS_VREDRAW;                                 //style:窗体类的风格,|将不同风格组合一起
//CS_HREDRAW|CS_VREDRAW:一旦移动或尺寸调整使客户区的宽度/高度发生变化,就重新绘制窗口,窗口在很多时候能被绘制或重新绘制,如在窗口创建、大小变更、从其他窗口后面移出窗口、最大或最小化等的时候。在窗口内容因某项操作改变时,
//系统将向程序发出WM_PAINT消息,通知程序作出相应的绘制工作,绘制操作前需调用BeginPaint函数获取图形显示的Device Context,而在绘制工作结束后调用EndPaint释放该Device Context。
//当然图形绘制工作也能在其他事件消息(如键盘或鼠标事件)发生时进行,这事绘图前需要调用GetDC或GetDCEx函数来获取图形显示的Device Context。





//注册窗体实例类wClass,注册窗体:RegisterClassEx(&wClass)
if(!RegisterClassEx(&wClass))              //如果窗体类注册不成功,函数返回0
{
	int nResult=GetLastError();                   //GetLastError()函数捕获错误码
	MessageBox(NULL,                               //弹出一个消息框,当然MessageBox()函数也是返回被单击按钮的值(类型是整数)
		"对不起,窗体类注册失败",
		"窗体类错误",
		MB_ICONERROR);
}



//创建窗体
HWND hWnd=CreateWindowEx(NULL,
	"window class",                      //还有很多类型,如EDIT编辑框等
	"这是一个简单的窗体程序",
	WS_OVERLAPPEDWINDOW,
//WS_OVERLAPPEDWINDOW:可以创建一个拥有各种窗口风格的窗体,包括标题,系统菜单,边框,最小化和最大化按钮等。
	200,                                  //窗体的横坐标,注意:如果你要再搞一个窗体或者其他什么,一定要比横坐标+宽度大,要不会重叠,纵坐标和高度同理,他的原点在左上角
	200,                                 //窗体的纵坐标
	640,                                 //窗体的宽度
	480,                                   //窗体的高度
	NULL,
	NULL,
	hInst,                                //实例句柄
	NULL);

//如果创建不成功,函数返回值将为0,可以用和前面一样的不成功时类似的错误处理代码,如下
if(!hWnd)
{
	int nResult=GetLastError();
	MessageBox(NULL,
		"创建窗体过程发生错误",
		"创建窗体失败",
		MB_ICONERROR);
}


//显示窗体,但还不会显示,还有回调函数
ShowWindow(hWnd,nShowCmd);


//创建窗体回调函数(处理来自窗体的所有信息)
//第一步:为程序添加一个主循环
MSG msg;               //用MSG定义一个消息msg
ZeroMemory(&msg,sizeof(MSG));                  //将变量msg分配到的内存块先清除掉
//令人不解的是,一般我们定义一个变量并不需要将变量值的地址的内存清空,直接使用就好了,为什么这里要使用ZeroMemory()清空呢?
//解答:如果不使用ZeroMemory(),可能结构体内的成员会得到系统赋的默认值,那样可能会出现意想不到的结果.所以,对于结构体进行清零是有必要的,然后对相应的成员赋其需要的值.

while(GetMessage(&msg,NULL,0,0))                      //用GetMessage()读取消息放入msg
//参数hwnd设置为NULL,具有特殊的含义,GetMessage 会获取线程中的所有窗口的消息,以及通过PostThreadMessage投递的消息。
//当接受到WM_DESTROY消息后,销毁窗口,但是进程不会退出,直到收到了WM_QUIT消息。如果GetMessage的参数是hwnd,那么只接受属于这个窗口的消息,当窗口销毁后,不能接受WM_QUIT消息,程序永远不会退出。
{
	TranslateMessage(&msg);             //转换消息放入msg
	DispatchMessage(&msg);             //向回调函数发送消息msg

}
return 0;
}

//第二步:回调函数的形式如下
LRESULT CALLBACK WinProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)        //WinProc是窗体回调函数的名称,可以自由定义,//参数:窗口句柄,消息,消息参数,消息参数
//hWnd:标识调用回调函数的窗体句柄;msg:标识hWnd窗体要处理的消息;wParam:一个32位的消息参数,其含义和数值根据消息的不同而不同;lParam:一个32位的消息参数,其值与消息有关
{
switch(msg)
{
case WM_DESTROY:                //这个回调函数只侦听WM_DESTROY消息并作出相应,这个是用户点击了窗体右上角的×按钮触发的
	{
		PostQuitMessage(0);
//PostQuitMessage寄送一个WM_QUIT消息给线程的消息队列并立即返回;此函数向系统表明有个线程请求在随后的某一时间终止。 当线程从消息队列里取得WM_QUIT消息时,应当退出消息循环并将控制返回给系统。
//返回给系统的退出值必须是消息WM_QUIT的wParam参数。 而消息循环函数GetMessage只有在接收到WM_QUIT消息时,返回值才为0,
//其他消息均返回非零值,如果出现错误,返回值为-1,所以此处的0就表示传递进去的是WM_QUIT消息,不能换成1,换成1将导致传递的消息不明确;参数这个是自己设置的,你不要系统也会返回一个默认值
		return 0;
	}
	break;
}
return DefWindowProc(hWnd,msg,wParam,lParam);           //其他消息交给由系统提供的缺省处理函数
}

猜你喜欢

转载自blog.csdn.net/weixin_42717928/article/details/82527703