上次,我们一起写了一个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程序有所理解,希望大家多实践,如果有暂时不明白的内容,可以先放下,因为主要的内容,我已经告诉你们了,次要的后边会根据需要讲解。

第2个Wiindows程序讲解的更多相关文章

  1. OpenGL学习-------visual studio 2010配置和第一个OpenGL程序讲解

    OpenGL作为当前主流的图形API之一,它在一些场合具有比DirectX更优越的特性. 1.与C语言紧密结合. OpenGL命令最初就是用C语言函数来进行描述的,对于学习过C语言的人来讲,OpenG ...

  2. 第一个Windows程序讲解

    上次,我们一起写了第一个Windows程序,虽然程序非常简单,但是它却可以帮助大家建立学好windows开发的信心. 今天,就让我帮助大家分析一下这个程序的内容.首先,我们的程序包含了一个头文件:wi ...

  3. fpga Verilog hdl 按键消抖 部分程序讲解

    module debounce(clk_in,rst_in,key_in,key_pulse,key_state); input clk_in;//system clock input rst_in; ...

  4. C#可定制的数据库备份和恢复程序 (讲解流程)

    可定制的数据库备份和恢复程序 tashanzhishi [原作] 关键字 数据库 备份 恢复 出处 在我们做数据库系统的程序时,经常需要为客户做一个数据库的备份和恢复程序,特别是对于一些非专业的数据库 ...

  5. C指针(3)——指向指针的指针(程序讲解)

    int **q可以分成两部分,即int* 和 (*q),后面的 “q” 中的* 表示q是一个指针变量,前面的int*表示指针变量q只能存放int*型变量的地址.int** q表示为指针变量q只能存放i ...

  6. C指针(2)——指针在函数中的应用(程序讲解)

    3-1.c指针用作函数参数 #include<stdio.h> typedef unsigned char uint8_t; //类型自定义,通过typedef语句重新把unsigned ...

  7. C指针(1)——指针在数组中的应用(程序讲解)

    2-1.c数组指针的定义: #include <stdio.h> int main() { char str[]="China Beijing Fujian"; //定 ...

  8. php微信支付接口开发程序

    php微信支付接口开发程序讲解 微信支付接口现在也慢慢的像支付宝一个可以利用api接口来实现第三方网站或应用进行支付了, 下文整理了一个php微信支付接口开发程序并且己测试,有兴趣的朋友可进入参考. ...

  9. iBrand 开源电商小程序 (Laravel API+ webpack + gulp + 原生小程序)

    iBrand 社交电商产品正式进入开源过程中了,我们制定了详细的开源计划,目前已经发布了 V1 的版本,后续的版本也在陆续整理完善中. 各个版本功能明细如下图: 3 个版本计划在今年春节前全部完成,可 ...

随机推荐

  1. jq實現網頁個性title

    <!DOCTYPE html> <html content="text/html; charset=UTF-8"> <title>tooltip ...

  2. 模板-->中国剩余定理[互质版本]

    如果有相应的OJ题目,欢迎同学们提供相应的链接 相关链接 所有模板的快速链接 扩展欧几里得extend_gcd模板 poj_1006_Biorhythms,my_ac_code 简单的测试 None ...

  3. jquery.placeholder.js的使用

    最近做东西用到placeholder这个属性,可是这个属性在低版本的IE或者QQ浏览器等这些浏览器上这个属性不能生效,后来在网上查了下,发现了jquery的一个插件jquery.placeholder ...

  4. linux 'more' command.

    more命令,功能类似 cat ,cat命令是整个文件的内容从上到下显示在屏幕上. more会以一页一页的显示方便使用者逐页阅读,而最基本的指令就是按空白键(space)就往下一页显示,按 b 键就会 ...

  5. c#读取通达信历史数据的方法

    public Bar ReadBarMin(BinaryReader br, int instrumentId, long size) { int date = br.ReadUInt16(); in ...

  6. 基于GBT28181:SIP协议组件开发-----------第五篇SIP注册流程eXosip2实现(二)

    原创文章,引用请保证原文完整性,尊重作者劳动,原文地址http://www.cnblogs.com/qq1269122125/p/3966794.html. 上章节讲解了讲解一个用eXosip2库实现 ...

  7. 你好,C++(11)如何用string数据类型表示一串文字?根据初始值自动推断数据类型的auto关键字(C++ 11)

    3.5.2  字符串类型 使用char类型的变量我们可以表示单个字符,那么,我们又该如何表示拥有多个字符的字符串呢? 我们注意到,一个字符串是由多个字符串连起来形成的.很自然地,一种最简单直接的方法就 ...

  8. Vim中安装delimitMate,auto-pairs插件不能输入中文问题

    在安装了delimitMate插件之后发现不能正常使用中文输入了,输入法系统是ibus. 解决办法是在ibus的设置中的“在应用程序中使用内嵌编辑模式”这一项去除就可以正常输入中文了,看来可能是ibu ...

  9. 03_RHEL7.1去掉注册提示

    # rpm –qa|grep subscription-manager 出现类似下面的代码: subscription-manager-firstboot-1.13.22-1.el7.x86_64 s ...

  10. 如何写angularJS模块

    angularJS中提供模块的概念,供我们把代码封装在模块单元中,使用模块能给我们带来的好处 保持全局命名空间的清洁 易于在不同应用间复用代码 demo.html <!doctype html& ...