第三章 Win32应用程序设计.docx

上传人:b****5 文档编号:7238944 上传时间:2023-01-22 格式:DOCX 页数:34 大小:72.77KB
下载 相关 举报
第三章 Win32应用程序设计.docx_第1页
第1页 / 共34页
第三章 Win32应用程序设计.docx_第2页
第2页 / 共34页
第三章 Win32应用程序设计.docx_第3页
第3页 / 共34页
第三章 Win32应用程序设计.docx_第4页
第4页 / 共34页
第三章 Win32应用程序设计.docx_第5页
第5页 / 共34页
点击查看更多>>
下载资源
资源描述

第三章 Win32应用程序设计.docx

《第三章 Win32应用程序设计.docx》由会员分享,可在线阅读,更多相关《第三章 Win32应用程序设计.docx(34页珍藏版)》请在冰豆网上搜索。

第三章 Win32应用程序设计.docx

第三章Win32应用程序设计

第三章Win32应用程序设计

在过去,进行Windows程序设计是一件痛苦异常的事情,原因是那时候还没有现在的这些设计精美的应用程序开发工具。

在今天,一个对Windows程序运行的内部机制几乎一无所知的初入门者,只需要通过不到一天的学习,也可以使用如VisualBasic之类的程序开发工具创建出功能完整的Windows应用程序。

这在几年前还是一件不可思议的事,因为即使是一个熟练掌握C语言的程序员,在当时差不多需要半年的学习才可以较全面的掌握Windows的编程技术,而且,与在DOS环境下编程相比,急剧膨胀的程度代码大大增加了程序调试的困难,从而使得编写一个出色的Windows应用程序要比编写一个出色的DOS需要考虑多得多的东西。

在Microsoft的另一种易学易用的编程工具VisualBasic中,从某种角度说,Windows程序不是编出来的,而是由程序员画出来的。

但是要知道,一个出色的Windows的应用程序并不仅在于在屏幕上绘出程序的各个窗口和在窗口中恰当的安排每一个控件。

对于具有一定基础的程序员而言,更重要的内容在于知道Windows和Windows应用程序的运行机制,以及它们之间以何种方式来进行通信,然而,明确自己在编写Windows时所需做的工作是哪一些。

换句话说,我们需要透过Windows漂亮的图形用户界面,认清在底层所发生的每一件事情。

然而,这并非是一件容易的事。

虽然,使用MFC和AppWizard,我们仍可能只需要回答几个简单的问题和添加少数的几条代码就能够生成功能完整的Windows应用程序。

但是记住,没有一个成功的商业软件是使用这样的方式生成的。

同时,也只有深入的理解了MFC应用程序框架的运行机制,才可能用好和用活这一工具,才能达到熟悉掌握VisualC++的境界。

尽管说MFC应用程序框架提供的是面向对象的Windows编程接口,这和传统的使用C语言和SDK来进行的Windows应用程序设计有着很大的不同,但是从底层来说,其中的大部分功能仍是通过调用最基本的Win32API来实现的。

其中最重要的一点是,Windows应用程序的运行机制仍然没有改变,它们仍然是通过消息来和操作系统,进而和用户进行交互的事件驱动的应用程序。

MFC对这一切进行了比较彻底的封装,它们隐藏在你所看不见的背面。

即使你对这一切一无所知,你仍可以在VisualC++中使用MFC来进行程序设计。

但是,经验表明,理解这一切的最好的方式是回过头去,看一看这些内容在SDK编写的应用程序是如何实现的,然后,再看一看在MFC中是如何把它们一层一层的与程序员隔离开的。

因此,在本章中介绍相对已“过时”的Win32SDK编程,并非是说以后也使用SDK来编写应用程序,而在于让你通过它们更深入的从MFC的内部了解MFC,并且,对于某些术语和概念的说明和澄清,也有助于你以后理解很多的东西。

如果你一开始对这些东西不感兴趣,那么,你可以先暂时跳过此章,继续阅读本书的其它部分。

当你对于MFC中的某些问题感到不解,或者想知道MFC究竟是如何工作的时候,再回过头来补充这些知识,也是完全可以的。

本章包括以下的内容:

lWindows应用程序的消息处理

lWin32API和SDK

lWinMain函数

l窗口和窗口过程

l32位编程的特点

第一节事件驱动的应用程序

类似的话已在很多书籍中说过了无数遍,以至于每一个正在或试图进行Windows编程的人都耳熟能详:

Windows应用程序是事件驱动(或称作消息驱动)的应用程序。

Windows是一个多任务的操作系统,也就是说,在同一时刻,在Windows中有着多个应用程序的实例正在运行,比如说这时我正在打开字处理软件Word来编写这本书的书稿,同时,还打开了VisualC++的集成开发环境MicrosoftDeveloperStudio来调试书中的示例程序,而且,后台还在放着歌曲。

在这样的一个操作系统中,不可能像过去的DOS那样,由一个应用程序来享用所有的系统资源,这些资源是由Windows统一管理的。

那么,特定的应用程序如何获得用户输入的信息呢?

事实上,Windows时刻监视着用户的一举一动,并分析用户的动作与哪一个应用程序相关,然后,将用户的动作以消息的形式发送给该应用程序,应用程序时刻等待着消息的到来,一但发现它的消息队列中有未处理的消息,就获取并分析该消息,最后,应用程序根据消息所包含的内容采取适当的动作来响应用户所作的操作。

举一个例子来说明上面的问题,假设我们编了一个程序,该程序有一个File菜单,那么,在运行该应用程序的时候,如果用户单击了File菜单,这个动作将被Windows(而不是应用程序本身!

)所捕获,Windows经过分析得知这个动作应该由上面所说的那个应用程序去处理,既然是这样,Windows就发送了个叫做WM_COMMAND的消息给应用程序,该消息所包含的信息告诉应用程序:

“用户单击了File菜单”,应用程序得知这一消息之后,采取相应的动作来响应它,这个过程称为消息处理。

Windows为每一个应用程序(确切地说是每一个线程)维护了相应的消息队列,应用程序的任务就是不停的从它的消息队列中获取消息,分析消息和处理消息,直到一条接到叫做WM_QUIT消息为止,这个过程通常是由一种叫做消息循环的程序结构来实现的。

Windows所能向应用程序发送的消息多达数百种,但是,对于一般的应用程序来说,只是其中的一部分有意义,举一个例子,如果你的应用程序只使用鼠标,那么如WM_KEYUP、WM_KEYDOWN和WM_CHAR等消息就没有任何意义,也就是说,应用程序中事实上不需要处理这些事件,对于这些事件,只需要交给Windows作默认的处理即可。

因此,在应用程序中,我们需要处理的事件只是所有事件中的一小部分。

图3.1给出了一般Windows应用程序的执行流程。

图3.1Windows应用程序的基本流程

因此,从某种角度上来看,Windows应用程序是由一系列的消息处理代码来实现的。

这和传统的过程式编程方法很不一样,编程者只能够预测用户所利用应用程序用户界面对象所进行的操作以及为这些操作编写处理代码,却不可以这些操作在什么时候发生或者是以什么顺序来发生,也就是说,我们不可能知道什么消息会在什么时候以什么顺序来临。

Windows程序在处理消息时使用了一种叫做回调函数(callbackfunction)的特殊函数。

回调函数由应用程序定义,但是,在应用程序中并没有调用回调函数的代码,回调函数是供操作系统或者其子系统调用的,这种调用通常发生在某一事件发生,或者在窗口或字体被枚举时。

典型的回调函数有窗口过程、对话框过程和钩子函数。

其中的窗口过程和对话框过程将在本章后面的内容中讲述。

第二节Win32API和SDK

说到Windows编程,就不能不谈到WindowsAPI(WindowsApplicationProgrammingInterface,Windows应用程序编程接口),它是所有Windows应用程序的根本之所在。

简单的说,API就是一系列的例程,应用程序通过调用这些例程来请求操作系统完成一些低级服务。

在Windows这样的图形用户界面中,应用程序的窗口、图标、菜单和对话框等就是由API来管理和维护的。

WindowsAPI具有两种基本类型:

Win16API和Win32API。

两者在很多方面非常相像,但是Win32API除了几乎包括了Win16API中的所有内容以外,还包括很多的其它内容。

WindowsAPI依靠三个主要的核心组件提供Windows的大部分函数,在Win16和Win32中,它们具有不同的名称,如表3.1所示。

Win16API

Win32API

说明

USER.EXE

USER32.DLL

负责窗口的管理,包括消息、菜单、光标、通信、计时器和其它与控制窗口显示

GDI.EXE

GDI32.DLL

提供图形设备接口,管理用户界面和图形绘制,包括Windows元文件、位图、设备描述表和字体等

KRNL386.EXE

KERNEL32.DLL

处理存储器低层功能、任务和资源管理等Windows核心服务

表3.1Win16和Win32的核心组件

虽然Win16API组件带有.EXE的扩展名,但是它们事实都是动态链接库(.DLL),不能单独运行。

其它一些非核心的WindowsAPI由其它组件所提供的DLL来实现,这些组件包括通用对话框、打印、文件压缩、版本控制以及多媒体支持等。

WindowsSDK(WindowsSoftwareDevelopmentKit,Windows软件开发工具包)和WindowsAPI紧密联系,它是一套帮助C语言程序员创建Windows应用程序的工具,在WindowsSDK中包括了以下几个组成部分:

l大量的在线帮助,这些帮助描述了Windows编程所可能用到的函数、消息、结构、宏及其它资源

l各种编程工具,如对话框编辑器及图象编辑器等

lWindows库及头文件

l使用C语言编写的示例程序

该工具包的最新版本就是我们正在使用的Win32SDK,在安装了VisualC++的同时,Win32SDK也安装到你的计算机上了。

尽管MFC提供了对Win32API的比较完整的封装,但是,在某些情况下,我们更倾向于直接调用Win32API,因为这有时候可以获得更高的效率,并且有着更大的自由度。

而且,使用MFC编写的新风格的Windows应用程序的工作方式基本上与使用SDK编写的同一程序一样,它们往往有着很多的共同之处,只是使用MFC更加的方便,因为它隐藏了大量的复杂性。

前面提到过,面向对象的编程方式是当前最流行的程序设计方法,但是,Win32API本身却是基于C语言的过程式编程的,SDK和MFC的最主要的不同之处也就是以C与C++之间的差别,使用MFC进行Windows应用程序设计需要面向对象的编程思想和方法,好在我们已经在前面这此进行了大量的铺垫。

第三节使用SDK编写Windows应用程序

传统的DOS程序以main函数作为进入程序的初始入口点,在Windows应用程序中,main函数被WinMain函数取而代之,WinMain函数的原型如下:

intWINAPIWinMain(HINSTANCEhInstance,//当前实例句柄

HINSTANCEhPrevInstance,//前一实例句柄

LPSTRlpCmdLine,//指向命令行参数的指针

intnCmdShow)//窗口的显示状态

这里出现了一个新的名词“句柄”(handle),所谓的句柄是一个标识对象的变量,或者是一个对操作系统资源的间接引用。

在上面的函数原型中,我们看到了一些“奇怪”的数据类型,如前面的HINSTANCE和LPSTR等,事实上,很多这样的数据类型只是一些基本数据类型的别名,表3.2列出了一些在Windows编程中常用的基本数据类型的别名,表3.3列出了常用的预定义句柄,它们的类型均为void*,即一个32位指针。

表3.2Windows基本数据类型

Windows的数据类型

对应的基本数据类型

说明

BOOL

Int

布尔值

BSTR

unsignedshort*

32位字符指针

BYTE

unsignedchar

8位无符号整数

COLORREF

unsignedlong

用作颜色值的32位值

DWORD

unsignedlong

32位无符号整数,段地址和相关的偏移地址

LONG

Long

32位带符号整数

LPARAM

Long

作为参数传递给窗口过程或回调函数的32位值

LPCSTR

constchar*

指向字符串常量的32位指针

LPSTR

char*

指向字符串的32位指针

LPCTSTR

constchar*(注1)

指向可移植的Unicode和DBCS字符串常量的32位指针

LPTSTR

char*(注1)

指向可移植为Unicode和DBCS字符串的32位指针

LPVOID

void*

指向未定义类型的32位指针

LRESULT

Long

来自窗口过程或回调函数的32位返回值

UINT

unsignedint

32位无符号整数

WNDPROC

long(_stdcall*)(void*,

unsignedint,unsignedint,long)(注2)

指向窗口过程的32位指针

WORD

unsignedshort

16位无符号整数

WPARAM

unsignedint

当作参数传递给窗口过程或回调函数的32位值

注1:

这是在DBCS版本下的情况,在Unicode版本下LPCTSTR和LPTSTR将代表其它的数据类型。

注2:

事实上,WNDPROC被定义为LRESULT(CALLBACK*)(HWND,UINT,WPARAM,LPARAM),这个定义最终被编译器解释为long(__stdcall*)(void*,unsignedint,unsignedint,long)。

表3.3Windows公用句柄类型

句柄类型

说明

HBITMAP

保存位图信息的内存域的句柄

HBRUSH

画刷句柄

HCTR

子窗口控件句柄

HCURSOR

鼠标光标句柄

HDC

设备描述表句柄

HDLG

对话框句柄

HFONT

字体句柄

HICON

图标句柄

HINSTANCE

应用程序的实例句柄

HMENU

菜单句柄

HMODULE

模块句柄

HPALETTE

颜色调色板句柄

HPEN

在设备上画图时用于指明线型的笔的句柄

HRGN

剪贴区域句柄

HTASK

独立于已执行任务的句柄

HWND

窗口句柄

查看Win32SDK文档或者浏览Windows头文件(如windef.h、ctype.h以及winnt.h等)可以获得关于其它数据类型的定义,这些定义往往使用了#define和typedef等关键字。

这里解释什么是应用程序的一个实例(instance)。

最简单的理解可以用下面的例子来说明:

比如说已经在Windows中打开了一个“写字板”(可以在“开始”菜单中的“程序|附件”下面找到它的快捷方式),现在你需要从另一篇文章里复制一部分内容到你正在写的这篇文章中,那么,你可以再打开一个“写字板”(注意写字板不是一个多文档应用程序,不能像在Word中那样打开多个不同的文件),然后从该写字板中复制文件的内容到在前一个写字板内打开的文章中。

这里,我们多次运行了同一个应用程序,在这个例子中,我们将所打开的两个写字板叫做该应用程序的两个实例。

对于实例的更精确(当然也要比上面的例子要更难懂得多)的定义,在Win32SDK中是这样给出的:

实例就是类中一特定对象类型的一个实例化对象(instantiation),如一个特定的进程或线程,在多任务操作系统中,一个实例指所加载的应用程序或动态链接库的一份拷贝。

刚开始时我们也许看不懂这一定义,不过没有关系,慢慢的就理解了。

l注意:

l尽管在前面给出的WinMain函数的原型中包括了一个名为hPrevInstance的HINSTANCE类型的参数,按照其字面上的意义,它所传递的是应用程序的前一个实例的句柄,但是,在Win32平台下,该参数的值总是为NULL,而不管是否有当前应用程序的实例在运行。

在过去的Windows3.x环境下编程,我们常常使用下面的代码来检查应用程序是否已有一个实例在运行:

lif(!

hPrevInstance)

l{

l//在此添加没有应用程序实例在运行时的所需执行的代码。

l//对于大多数应用程序,我们常在这里注册窗口类。

l}

然而,在Win32操作系统——Windows95、WindowsNT以及其后续版本中,上面的if条件体中的代码总会被执行,因为hPrevInstance总是为NULL,因此!

hPrevInstance恒为真。

之所以这样,是因为在Win32环境下,每一个应用程序的实例都有自已独立的地址空间,因此,它们之间互相独立,互不干涉。

但是,对于一些应用程序,只需要而且只应该有一个实例在运行。

什么情况下会是这样呢?

假设我们编写了一个应用程序,在默认情况下,该应用程序将在后台运行,通过按下程序所定义的某一个热键,应用程序将被激活。

对于这样的应用程序,在同一时该只应该有一个实例在运行。

另外,像WindowsNT下在任务管理器,在同一时刻也只可以有一个实例在运行。

使用下面的技巧可以保证在同一时刻只有应用程序的一个实例:

l#include"windows.h"

l

l#defineVK_X0x58

l

lintWINAPIWinMain(HINSTANCEhInstance,

lHINSTANCEhPrevInstance,

lLPSTRlpCmdLine,

lintnCmdShow)

l{

lif(!

CreateMutex(NULL,TRUE,"NoPreviousInstance!

"))

l{

lMessageBox(NULL,"创建Mutex失败!

","NoPrev",MB_OK|MB_SYSTEMMODAL);

lreturnFALSE;

l}

lif(GetLastError()==ERROR_ALREADY_EXISTS)

l{

lMessageBox(NULL,"已有NoPrev的一个实例在运行,当前实例将被终止!

",

l"NoPrev",MB_OK|MB_SYSTEMMODAL);

lreturnFALSE;

l}

lif(!

RegisterHotKey(NULL,0x0001,MOD_CONTROL|MOD_SHIFT,VK_X))

l{

lMessageBox(NULL,"注册热键Ctrl+Shift+X失败!

",

l"NoPrev",MB_OK|MB_SYSTEMMODAL);

lreturnFALSE;

l}

lMessageBox(NULL,"NoPrev已启动!

\n\n按下热键Ctrl+Shift+X将终止NoPrev.",

l"NoPrev",MB_OK|MB_SYSTEMMODAL);

lMSGmsg;

lwhile(GetMessage(&msg,NULL,0,0))

l{

lswitch(msg.message)

l{

lcaseWM_HOTKEY:

lif(int(msg.wParam)==0x0001)

lif(MessageBox(NULL,"终止NoPrev?

",

l"NoPrev",MB_YESNO|MB_SYSTEMMODAL)==IDYES)

lreturnTRUE;

l}

l}

lreturnTRUE;

l}

上面的代码是一个功能完整的Windows应用程序,其中用到了一些到目前为止我们还未讲述到的内容。

程序定义了热键Ctrl+Shift+X,当按下该热键时将终止该程序。

由于程序中没有包括任何窗口,因此这是唯一的一种正常终止应用程序的方法。

当程序NoPrev正在后台运行时,如果用户按下了组合键Ctrl+Shift+X,Windows将向程序主线程的消息队列中发送一条称为WM_HOTKEY的消息,当程序收到这条消息时,即弹出了消息框询问是否终止NoPrev。

上面的说明将有助于你理解以上代码,但是我们目前对止并不做要求。

这里,只需要注意下面的代码:

lif(!

CreateMutex(NULL,TRUE,"NoPreviousInstance!

"))

l{

lMessageBox(NULL,"创建Mutex失败!

","NoPrev",MB_OK|MB_SYSTEMMODAL);

lreturnFALSE;

l}

lif(GetLastError()==ERROR_ALREADY_EXISTS)

l{

lMessageBox(NULL,"已有NoPrev的一个实例在运行,当前实例将被终止!

",

l"NoPrev",MB_OK|MB_SYSTEMMODAL);

lreturnFALSE;

l}

在上面的代码中,我们先调用CreateMutex创建一个名为“NoPreviousInstance”的命名互斥对象(namedmutexobject),如果该对象名已存在(注意这时函数CreateMutex仍返回真值TRUE),则随后调用的GetLastError函数将返回ERROR_ALREADY_EXISTS,由此得知已有一个应用程序的实例正在运行。

从而弹出消息框提醒用户,然后终止应用程序的当前实例。

在上面的WinMain函数原型中的另一个奇怪的标识符为WINAPI,这是一个在windef.h头文件中定义的宏,在当前版本Win32SDK中,WINAPI被定义为FARPASCAL,因此,使用FARPASCAL同使用WINAPI具有同样的效果,但是,我们强烈建议你使用WINAPI来代替以前常用的FARPASCAL,因为Microsoft不保证FARPASCAL能够在将来的Windows版本中正常工作。

在目前情况下,和FARPASCAL等价的标识符还有CALLBACK(用在如窗口过程或对话框过程之类的回调函数前)和APIENTRY等。

它们对编译器而言都是一回事,最终将被解释为__stdcall。

在Windows环境下编程,会遇到很多这样的情况,注意不要混淆它们。

一般情况下,我们应该在WinMain函数中完成下面的操作:

1.注册窗口类;

2.创建应用程序主窗口;

3.进入应用程序消息循环。

接下来我们将依次讨论这些内容。

在Windows应用程序中,每一个窗口都必须从属于一个窗口类,窗口类定义了窗口所具有的属性,如它的样式、图标、鼠标指针、菜单名称及窗口过程名等。

在注册窗口类前,我们先创建一个类型为WNDCLASS的结构,然后在该结构对象中填入窗口类的信息,最后将它传递给函数RegisterClass,整个过程如下面的代码所示:

WNDCLASSwc;

//填充窗口类信息

wc.style=CS_HREDRAW|CS_VREDRAW;

wc.lpfnWndProc=WndProc;

wc.cbClsExtra=0;

wc.cbWndExtra=0;

wc.hInstance=hInstance;

wc.hIcon=LoadIcon(NULL,IDI_APPLICATION);

wc.hCursor=LoadCursor(NULL,IDC_ARROW);

wc.hbrBackground=GetStockObject(WHITE_BRUSH);

wc.lpszMenuName=NULL;

wc.lpszClassName="SdkDemo1";

//注册窗口类

RegisterClass(&wc);

下面解释一下结构WNDCLASS中各成员的含义:

style:

指定窗口样式。

该样式可以为一系列屏蔽位的按位或,在前面的例子中,CS_HREDRAW表示当窗口用户区宽度改变时重绘整个窗口,而CS_VREDRAW则表则表示当窗口用户区高度改变时重绘整个窗口。

对于其它的窗口样式,请参阅SDK中关于WNDCLASS的联机文档。

(顺便说一句,请注意该成员的大小写,它是小写的

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 解决方案 > 商业计划

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1