友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!
C语言实例教程(PDF格式)-第11部分
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部! 如果本书没有阅读完,想下次继续接着阅读,可使用上方 "收藏到我的浏览器" 功能 和 "加入书签" 功能!
倾向于直接调用Win32 API,因为这有时候可以获得更高的效率,并
且有着更大的自由度。而且,使用MFC编写的新风格的Windows应用程
序的工作方式基本上与使用SDK编写的同一程序一样,它们往往有着
很多的共同之处,只是使用MFC更加的方便,因为它隐藏了大量的复
杂性。
…………………………………………………………Page 122……………………………………………………………
前面提到过,面向对象的编程方式是当前最流行的程序设计方法,但
是,Win32 API本身却是基于C语言的过程式编程的,SDK和MFC的最主
要的不同之处也就是以C与C++之间的差别,使用MFC进行Windows应用
程序设计需要面向对象的编程思想和方法,好在我们已经在前面这此
进行了大量的铺垫。
第三节 使用SDK编写Windows应用程序
传统的DOS程序以main函数作为进入程序的初始入口点,在Windows应
用程序中,main函数被WinMain函数取而代之,WinMain函数的原型如
下:
int WINAPI WinMain (HINSTANCE hInstance; // 当前实例句柄
HINSTANCE hPrevInstance; // 前一实例句柄
LPSTR lpCmdLine; // 指向命令行参数的指针
int nCmdShow) // 窗口的显示状态
这里出现了一个新的名词 “句柄”(handle),所谓的句柄是一个标识
对象的变量,或者是一个对操作系统资源的间接引用。
在上面的函数原型中,我们看到了一些 “奇怪”的数据类型,如前面
的HINSTANCE和LPSTR等,事实上,很多这样的数据类型只是一些基本
数据类型的别名,表3。2列出了一些在Windows编程中常用的基本数据
类型的别名,表3。3列出了常用的预定义句柄,它们的类型均为void
*,即一个32位指针。
表3。 2 Windows基本数据类型
Windows中 对应的基本数据 说明
所用的数 类型
据类型
BOOL int 布尔值
BSTR unsigned short 32位字符指针
*
BYTE unsigned char 8位无符号整数
COLORREF unsigned long 用作颜色值的32位值
DWORD unsigned long 32位无符号整数,段地址和相关的偏移地
址
…………………………………………………………Page 123……………………………………………………………
LONG long 32位带符号整数
LPARAM long 作为参数传递给窗口过程或回调函数的32
位值
LPCSTR const char * 指向字符串常量的32位指针
LPSTR char * 指向字符串的32位指针
LPCTSTR const char * 指向可移植的Unicode和DBCS字符串常量
(注1) 的32位指针
LPTSTR char *(注1) 指向可移植为Unicode和DBCS字符串的32
位指针
LPVOID void * 指向未定义类型的32位指针
LRESULT long 来自窗口过程或回调函数的32位返回值
UINT unsigned int 32位无符号整数
WNDPROC long 指向窗口过程的32位指针
(__stdcall *)
(void
*;unsigned
int;unsigned
int;long)(注2)
WORD unsigned short 16位无符号整数
WPARAM unsigned int 当作参数传递给窗口过程或回调函数的32
位值
注1: 这是在DBCS版本下的情况,在Unicode版本下LPCTSTR和LPTSTR将代表
其它的数据类型。
注2: 事实上,WNDPROC被定义为LRESULT (CALLBACK*)(HWND; UINT;
WPARAM; LPARAM),这个定义最终被编译器解释为long (__stdcall *)(void
*;unsigned int;unsigned int;long)。
表3。 3 Windows公用句柄类型
句柄类型 说明
HBITMAP 保存位图信息的内存域的句柄
HBRUSH 画刷句柄
HCTR 子窗口控件句柄
HCURSOR 鼠标光标句柄
…………………………………………………………Page 124……………………………………………………………
HDC 设备描述表句柄
HDLG 对话框句柄
HFONT 字体句柄
HICON 图标句柄
HINSTANCE 应用程序的实例句柄
HMENU 菜单句柄
HMODULE 模块句柄
HPALETTE 颜色调色板句柄
HPEN 在设备上画图时用于指明线型
的笔的句柄
HRGN 剪贴区域句柄
HTASK 独立于已执行任务的句柄
HWND 窗口句柄
查看Win32 SDK文档或者浏览Windows头文件 (如windef。h、ctype。h以
及winnt。h等)可以获得关于其它数据类型的定义,这些定义往往使用
了#define和typedef等关键字。
这里解释什么是应用程序的一个实例 (instance)。最简单的理解可以
用下面的例子来说明:比如说已经在Windows中打开了一个 “写字
板”(可以在 “开始”菜单中的 “程序 |附件”下面找到它的快捷方
式),现在你需要从另一篇文章里复制一部分内容到你正在写的这篇
文章中,那么,你可以再打开一个 “写字板”(注意写字板不是一个
多文档应用程序,不能像在Word中那样打开多个不同的文件),然后
从该写字板中复制文件的内容到在前一个写字板内打开的文章中。这
里,我们多次运行了同一个应用程序,在这个例子中,我们将所打开
的两个写字板叫做该应用程序的两个实例。对于实例的更精确 (当然
也要比上面的例子要更难懂得多)的定义,在Win32 SDK中是这样给出
的:实例就是类中一特定对象类型的一个实例化对象
(instantiation),如一个特定的进程或线程,在多任务操作系统
中,一个实例指所加载的应用程序或动态链接库的一份拷贝。刚开始
时我们也许看不懂这一定义,不过没有关系,慢慢的就理解了。
l 注意:
…………………………………………………………Page 125……………………………………………………………
l 尽管在前面给出的WinMain函数的原型中包括了一个名为
hPrevInstance的HINSTANCE类型的参数,按照其字面上的意义,
它所传递的是应用程序的前一个实例的句柄,但是,在Win32平台
下,该参数的值总是为NULL,而不管是否有当前应用程序的实例
在运行。在过去的Windows 3。x环境下编程,我们常常使用下面的
代码来检查应用程序是否已有一个实例在运行:
l if (!hPrevInstance)
l {
l // 在此添加没有应用程序实例在运行时的所需执行的代码。
l // 对于大多数应用程序,我们常在这里注册窗口类。
l }
然而,在Win32操作系统——Windows 95、Windows NT 以及其
后续版本中,上面的if条件体中的代码总会被执行,因为
hPrevInstance总是为NULL,因此!hPrevInstance恒为真。
之所以这样,是因为在Win32环境下,每一个应用程序的实例
都有自已独立的地址空间,因此,它们之间互相独立,互不
干涉。但是,对于一些应用程序,只需要而且只应该有一个
实例在运行。什么情况下会是这样呢?假设我们编写了一个
应用程序,在默认情况下,该应用程序将在后台运行,通过
按下程序所定义的某一个热键,应用程序将被激活。对于这
样的应用程序,在同一时该只应该有一个实例在运行。另
外,像Windows NT下在任务管理器,在同一时刻也只可以有
一个实例在运行。
使用下面的技巧可以保证在同一时刻只有应用程序的一个实
例:
l #include 〃windows。h〃
l
l #define VK_X 0x58
l
l int WINAPI WinMain (HINSTANCE hInstance;
…………………………………………………………Page 126……………………………………………………………
l HINSTANCE hPrevInstance;
l LPSTR lpCmdLine;
l int nCmdShow)
l {
l if (!CreateMutex(NULL;TRUE;〃No Previous Instance!〃))
l {
l MessageBox(NULL;〃创建Mutex失败!〃;〃NoPrev〃;MB_OK|MB_SYSTEMMODAL);
l return FALSE;
l }
l if (GetLastError()==ERROR_ALREADY_EXISTS)
l {
l MessageBox(NULL;〃已有NoPrev的一个实例在运行; 当前实例将被终止!〃;
l 〃NoPrev〃;MB_OK|MB_SYSTEMMODAL);
l return FALSE;
l }
l if(!RegisterHotKey(NULL;0x0001;MOD_CONTROL|MOD_SHIFT;VK_X))
l {
l MessageBox(NULL;〃注册热键Ctrl+Shift+X失败!〃;
l 〃NoPrev〃;MB_OK|MB_SYSTEMMODAL);
l return FALSE;
l }
l MessageBox(NULL;〃NoPrev已启动!nn按下热键Ctrl+Shift+X将终止NoPrev。〃;
l 〃NoPrev〃;MB_OK|MB_SYSTEMMODAL);
…………………………………………………………Page 127……………………………………………………………
l MSG msg;
l while (GetMessage(&msg;NULL;0;0))
l {
l switch (msg。message)
l {
l case WM_HOTKEY:
l if (int(msg。wParam)==0x0001)
l if (MessageBox(NULL;〃终止NoPrev?〃;
l 〃NoPrev〃;MB_YESNO|MB_SYSTEMMODAL)==IDYES)
l return TRUE;
l }
l }
l return TRUE;
l }
上面的代码是一个功能完整的Windows应用程序,其中用到了
一些到目前为止我们还未讲述到的内容。程序定义了热键
Ctrl+Shift+X,当按下该热键时将终止该程序。由于程序中
没有包括任何窗口,因此这是唯一的一种正常终止应用程序
的方法。当程序NoPrev正在后台运行时,如果用户按下了组
合键Ctrl+Shift+X,Windows将向程序主线程的消息队列中发
送一条称为WM_HOTKEY的消息,当程序收到这条消息时,即弹
出了消息框询问是否终止NoPrev。上面的说明将有助于你理
解以上代码,但是我们 目前对止并不做要求。这里,只需要
注意下面的代码 :
l if (!CreateMutex(NULL;TRUE;〃No Previous Instance!〃))
l {
…………………………………………………………Page 128……………………………………………………………
l MessageBox(NULL;〃创建Mutex失败!〃;〃NoPrev〃;MB_OK|MB_SYSTEMMODAL);
l return FALSE;
l }
l if (GetLastError()==ERROR_ALREADY_EXISTS)
l {
l MessageBox(NULL;〃已有NoPrev的一个实例在运行; 当前实例将被终止!〃;
l 〃NoPrev〃;MB_OK|MB_SYSTEMMODAL);
l return FALSE;
l }
在上面的代码 中,我们先调用CreateMutex创建一个名为 “
No
Previous Instance”的命名互斥对象(named mutex
object),如果该对象名已存在(注意这时函数CreateMutex仍
返回真值TRUE),则随后调用的GetLastError函数将返回
ERROR_ALREADY_EXISTS,由此得知已有一个应用程序的实例
正在运行。从而弹出消息框提醒用户,然后终止应用程序的
当前实例。
在上面的WinMain函数原型中的另一个奇怪的标识符为WINAPI,这是
一个在windef。h头文件中定义的宏,在当前版本Win32 SDK中,
WINAPI被定义为FAR PASCAL,因此,使用FAR PASCAL同使用WINAPI具
有同样的效果,但是,我们强烈建议你使用WINAPI来代替以前常用的
FAR PASCAL,因为Microsoft不保证FAR PASCAL能够在将来的Windows
版本中正常工作。在目前情况下,和FAR PASCAL等价的标识符还有
CALLBACK (用在如窗口过程或对话框过程之类的回调函数前)和
APIENTRY等。它们对编译器而言都是一回事,最终将被解释为
__stdcall。在Windows环境下编程,会遇到很多这样的情况,注意不
要混淆它们。
一般情况下,我们应该在WinMain函数中完成下面的操作:
1。 注册窗口类;
2。 创建应用程序主窗口;
…………………………………………………………Page 129……………………………………………………………
3。 进入应用程序消息循环。
接下来我们将依次讨论这些内容。
在Windows应用程序中,每一个窗口都必须从属于一个窗口类,窗口
类定义了窗口所具有的属性,如它的样式、图标、鼠标指针、菜单名
称及窗口过程名等。在注册窗口类前,我们先创建一个类型为
WNDCLASS的结构,然后在该结构对象中填入窗口类的信息,最后将它
传递给函数RegisterClass,整个过程如下面的代码所示:
WNDCLASS wc;
// 填充窗口类信息
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的联机文档。(顺便说一句,
请注意该成员的大小写,它是小写的style,而
…………………………………………………………Page 130……………………………………………………………
不是Style。)
lpfnWndProc: 指向窗口过程的指针。关于窗口
过程我们将以后面的内容中讲述。在前面的例子
中,我们使用名为WndProc的窗口过程。
cbClsExtra: 指定在窗口类结构之后分配的附加
字节数。操作系统将这些字节初始化为0。
cbWndExtra: 指定在窗口实例之后分配的附加字
节数。操作系统将这些字节初始化为0。如果应
用程序使用WNDCLASS结构注册一个使用资源文件
中的CLASS指令创建的对话框,那么cbWndExtra
必须被设置为DLGWINDOWEXTRA。
hInstance : 标识该类的窗口过程所属的实例。
hIcon : 标识类图标。该成员必须为一个图标资
源的句柄。如果该成员为NULL,则应用程序必须
在用户最小化应用程序窗口时绘制图标。
hCursor : 标识类鼠标指针。该成员必须为一个
光标资源的句柄,如果该成员为NULL,当鼠标移
进应用程序窗口时应用程序必须显式指定指针形
状。
hbrBackground: 标识类背景刷子。该成员可以
为一个用来绘制背景的画刷句柄,或者为标准系
统颜色值之一。
lpszMenuName: 指向一個以NULL结尾的字符串的
指针,该字符串指定了类菜单的资源名称。如果
在资源名称为的菜单为一个整数所标识,则可以
使用MAKEINTRESOURCE宏将其转换为一个字符
串;如果该成员为NULL,则属于该类的窗口无默
认菜单。
lpszClassName: 指向一个以NULL结尾的字符串
或为一个原子。如果该参数为一个原子,那么它
必须是一个使用GlobalAddAtom函数创建的全局
原子;如果为一个字符串,该字符器将成员窗口
类名。
…………………………………………………………Page 131……………………………………………………………
l 注意:
l 这里多次提到窗口类这一名词,但是它和前面常说的C++类没有任
何联 系。窗口类只表示了窗口的类型,它完全不是面向对象意义
上的类,因为它不支持面向对象技术中的继承及多态等。
在使用RegisterClass注册窗口类成功之后,即可以使用该窗口类创
建并显示应用程序的窗口。这个过程如下面的代码所示:
// 创建应用程序主窗口
hWnd=CreateWindow (〃SdkDemo1〃; // 窗口类名
〃第一个Win32 SDK应用程序〃; // 窗口标题
WS_OVERLAPPEDWINDOW; // 窗口样式
CW_USEDEFAULT; // 初始化 x 坐标
CW_USEDEFAULT; // 初始化 y 坐标
CW_USEDEFAULT; // 初始化窗口宽度
CW_USEDEFAULT; // 初始化窗口高度
NULL; // 父窗口句柄
NULL; // 窗口菜单句柄
hInstance; // 程序实例句柄
NULL); // 创建参数
// 显示窗口
ShowWindow(hWnd;SW_SHOW);
// 更新主窗口客户区
UpdateWindow(hWnd);
由于上述代码均加上了详尽的注释,这里仅作一些简单的说明和强
调。CreateWindow函数的原型是这样的:
HWND CreateWindow(LPCTSTR lpClassName; // 指向已注册的类名
LPCTSTR lpWindowName; // 指向窗口名称
DWORD dwStyle; // 窗口样式
…………………………………………………………Page 132……………………………………………………………
int x; // 窗口的水平位置
int y; // 窗口的垂直位置
int nWidth; // 窗口宽度
int nHeight; // 窗口高度
HWND hWndParent; // 父窗口或所有者窗口句柄
HMENU hMenu; // 菜单句柄或子窗口标识符
HANDLE hInstance; // 应用程序实例句柄
LPVOID lpParam; // 指向窗口创建数据的指针
);
在前面的示例中,我们对x、y、nWidth和nHeight参数都传递了同一
个值CW_USEDEFAULT,表示使用系统默认的窗口位置和大小,该常量
仅对于重叠式窗口 (即在dwStype样式中指定了
WS_OVERLAPPEDWINDOW,另一个常量WS_TILEDWINDOW有着相同的值)有
效。对于CreateWindows函数的其它内容,比如关于dwStyle参数所用
常量的详细参考请 自行参阅Win32 SDK中的文档。
l 注意:
l 尽管Windows 95是一个32位的操作系统,但是,其
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!