第2个Wiindows程序讲解

    上次,我们一起写了一个Windows窗口程序,这个窗口程序虽然非常简单,但是,代码仍然很多,相信,一定会有很多初学者看见这些代码而感到头疼。不用怕,现在,我们就一起来分析一下这些代码,相信通过我们共同的努力,一定可以克服这些难题。

     首先,我们要做的第一件事情就是包含windows.h头文件,这个不再解释了,因为,我们前面已经解释过。

     之后,我们定义了一个WinMain函数和一个窗口处理函数MainWndProc。

     相信,很多人读到窗口处理函数,就一定会发晕,不知道它是一个什么东东,如果大家想要明白窗口处理函数,就必须先明白Windows下的消息机制,现在,我先简要介绍一下什么是消息机制,如果想要深入研究的人,可以看看《Windows核心编程》,windows的消息机制是借由消息系统来完成的。

     Windows的消息系统是由3个部分组成的:

     1)消息队列:Windows为所有的应用程序都维护一个消息队列,应用程序必须从消息队列中获取消息,然后分派给某个窗口处理函数处理。

     2)消息泵:通过这个消息泵,应用程序可以从消息队列中获取消息,然后,再把它分派给适当的窗口,最终由该窗口的窗口处理函数来处理它。

     3)窗口处理函数:每个窗口都有一个窗口处理函数,它的任务就是处理发给该窗口的消息,处理完消息后,它通常要返回一个值给Windows,告诉Windows该消息处理是否成功。

      相信通过上面的了解,大家大概对消息机制会有一个整体的把握,下面,我将以一个键盘的按键消息为例,告诉大家这个消息是如何产生,流转,处理。

      当我们按下键盘上的一个字符后,比如a,这个时候,键盘的硬件会检测到这个动作,然后通知系统,系统知道后,会为它产生一个消息:WM_CHAR,之后,系统会将这个消息发送到当前程序的消息队列,比如当前程序是记事本,那么这个时候,记事本程序的消息队列中会出现一个WM_CHAR消息,当记事本程序的消息泵从消息队列提取到这个字符的按键消息后,会调用当前窗口的处理函数来处理这个字符消息,当窗口处理函数处理WM_CHAR时,会在屏幕上显示这个字符,我们这里,这个字符是a,这就是整个消息处理的全过程。

      好了,现在,我们言归正传,继续分析接下来的程序。WinMain程序,这个是所有Windows程序的入口点,它是必须要有的,上次,我们已经讲过,这里不再提及。

     接下来,我们来一起看一下这个WinMain函数都做了什么?

    为了帮助大家理解WinMain函数,我们这里先使用自然语言进行概括性的描述。   

int WINAPI WinMain(HINSTANCE hinstance,
	HINSTANCE hPrevInstance,
	LPSTR lpCmdLine,
	int nCmdShow)
{
        1、定义一个WNDCLASS或者WNDCLASSEX变量wcx;
        2、设置WNDCLASSEX变量wcx;
        3、利用wcx创建注册一个窗口类MainWClass,窗口类就好比一个模版,Windows系统会参考这个模版,为我们创建窗口;
        4、用我们创建的窗口类MainWClass,创建一个窗口;
        5、调用ShowWindow和UpdateWindow显示和更新窗口;
        6、创建一个消息泵,这个消息泵的任务就是不断从消息队列中提取消息,并将消息传递给当前窗口的处理函数,
        当消息泵接收到WM_QUIT退出消息后,消息泵就会退出;
        7、程序返回。       
 }
    好了,现在,我们继续分析WinMain函数的内容:

    1)定义一个WNDCLASS或者WNDCLASSEX变量wcx,这个目标是通过如下的代码来完成的:

WNDCLASSEX wcx;
    2)设置窗口类,具体实现是通过如下的代码:

wcx.cbSize = sizeof(wcx);           // 这个参数设置wcx的大小;
wcx.style = CS_HREDRAW |CS_VREDRAW; // 告诉系统,当窗口大小改变时,重绘整个窗口;
wcx.lpfnWndProc = MainWndProc;      // 告诉系统,当前窗口类的消息处理函数是MainWndProc;
wcx.cbClsExtra = 0;                 // 这个大家先不用管,设置为0就可以;
wcx.cbWndExtra = 0;                 // 这个大家也先不用管,设置为0就可以;
wcx.hInstance = hinstance;          // 告诉系统当前这个窗口属于哪个程序,系统会根据这个参数,将该窗口的消息发送到这个程序的消息队列;
wcx.hIcon = LoadIcon(NULL,IDI_APPLICATION); // 窗口图标:大家也先不用管,使用这个IDI_APPLICATION值,就可以;
wcx.hCursor = LoadCursor(NULL,IDC_ARROW); //设置这个窗口的光标样式,大家也先不用管,设置为IDC_ARROW,就可以;
wcx.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);  // 设置当前窗口的背景颜色,这里是白色WHITE_BRUSH;
wcx.lpszMenuName = NULL; //当前窗口的菜单,我们这个窗口没有菜单,所以,只需要设置为NULL,就可以;
wcx.lpszClassName = "MainWClass";  // 告诉系统,我们这个窗口的类名;
wcx.hIconSm = (HICON)LoadImage(hinstance, // 当前窗口的小图标,大家先这么写,先不用管具体什么意思,后面会介绍。
		MAKEINTRESOURCE(5),
		IMAGE_ICON,
		GetSystemMetrics(SM_CXSMICON),
		GetSystemMetrics(SM_CYSMICON),
		LR_DEFAULTCOLOR);

    3)利用wcx创建注册一个窗口类MainWClass,具体通过如下的代码来完成:

if (!RegisterClassEx(&wcx))
{
	return 1;
}

    4)用我们创建的窗口类MainWClass,创建一个窗口,具体通过如下代码来完成:

hwnd = CreateWindow(
		"MainWClass",        // 窗口类名
		"我们的第二个程序",  // 窗口标题 
		WS_OVERLAPPEDWINDOW, // 窗口样式  
		CW_USEDEFAULT,       // 水平位置X:默认 
		CW_USEDEFAULT,       // 垂直位置Y:默认
		CW_USEDEFAULT,       // 宽度:默认
		CW_USEDEFAULT,       // 高度:默认 
		(HWND)NULL,          // 父窗口:无 
		(HMENU)NULL,         // 菜单:使用窗口类的菜单 
		hinstance,           // 应用程序实例句柄 
		(LPVOID)NULL);       // 窗口创建时数据:无 
    5)调用ShowWindow和UpdateWindow显示和更新窗口,具体通过如下代码来完成:

ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
    6)创建一个消息泵,具体通过如下的代码来完成:

while ((fGotMessage = GetMessage(&msg, (HWND)NULL, 0, 0)) != 0 && fGotMessage != -1)
{
	TranslateMessage(&msg);
	DispatchMessage(&msg);
}
   7)程序返回,具体通过如下的代码来完成:

return msg.wParam; //这个msg通常是WM_QUIT,这个wParam是这个消息的退出码,通常由PostQuitMessage来设置,主要是通知系统,该程序是否正常退出。
    相信,通过上面这段描述,大家一定对这个例子中的WinMain函数有了一个整体的把握,现在,我们趁热打铁,再一起分析一下我们这个窗口类的窗口处理函数。

    现在,我先告诉大家窗口处理函数的格式,具体如下:

LRESULT CALLBACK 窗口处理函数名(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
    大家只需要记住,窗口处理函数的格式必须这么写,只有函数名可以改变,其它的都不可以改变,这个是系统规定的,记住就可以,其中WPARAM和LPARAM是Windows消息中的两个参数,当窗口处理函数被调用的时候,hwnd代表了当前的窗口,uMsg代表了当前的消息,在Windows中,每个消息都用一个整数来表示。wParam和lParam是这个消息中的两个参数。

    下面,我先给出消息处理函数的一般流程:

LRESULT CALLBACK 窗口消息处理函数名(
        HWND hwnd,
	UINT uMsg,
	WPARAM wParam,
	LPARAM lParam
	)
{
	switch (uMsg)
	{
	    case 消息1:
		{
                    消息处理函数1;
                    break;
                }
            case 消息2:
                {
                    消息处理函数2;
                    break;
                }
            ...
            default:
		return DefWindowProc(hwnd, uMsg, wParam, lParam);
	}
}
    上面描述了消息处理函数的一般流程,我们按照这个一般流程来讲解一下我们的窗口处理函数,具体如下:

    我们的窗口处理函数,只响应了一个消息WM_DESTROY,这个消息是在我们点击窗口的关闭按钮是产生的,当我们的消息泵接收到这个消息后,就会将它发给我们的窗口处理函数,然后,我们的窗口处理函数调用函数ExitThread,其中传递的是线程的退出码,线程正常退出,通常都设置为0,当执行完这个函数后,整个线程会结束运行,我们这里的线程只有一个,就是WinMian所在的线程,当这个线程退出后,整个程序就会结束运行。

   对于其它的消息,我们都使用系统提供的默认消息处理函数来处理,这个函数的原型如下:  

LRESULT DefWindowProc(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM IParam);
    在这个函数中,hWnd就是当前窗口的句柄,Msg就是当前要处理的消息,wParam和lParam就是当前消息中的两个参数。

    好了,今天讲了很多,相信,大家读完后,应该会对我们的第2个Windows程序有所理解,希望大家多实践,如果有暂时不明白的内容,可以先放下,因为主要的内容,我已经告诉你们了,次要的后边会根据需要讲解。 

   

   

原文地址:https://www.cnblogs.com/new0801/p/6176939.html