第1章 Windows概述
1.1 历史一瞥(A Little Bit of History)
1.1.3 Windows3.0和3.1
1.1.5 Windows98及其后继版本
开动了!系好安全带,我们就要上路了!在开始的时候,Windows编程可能会使你觉得头疼,但可以保证,一旦你自己动手写过几个程序之后,就不会那头痛了。事实上,你会开始喜欢上它。好了,下面就是让屏幕的一个窗口显示“Hello World”字样所需的完整代码。
#include <windows.h>
int WINAPI WinMain(HINSTANCE hlnstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nShowCmd)
{
MessageBox(NULL,"Hello World!","MsgBox",0);
return 0;
}
如果在集成开发环境(IDE)里输入这个程序,再编译和运行它,在屏幕上就会出现一个小的消息框,等待单击OK按钮后退出。如果决定自行输入源程序,在IDE里选择创建的project类型时,必须选择W32 Application而不是Win32 console Application。否则代码不会通过编译。
如你所见,首先不同的一点就是程序入口点不再是昔日的:
int main()
而是
int WINAPI WinMain(HINSTANCE hlnstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nShowCmd)
下面就来解释一下。
基本上可以忽略WINAPI。它只是WINDEF.h中定义的一个宏,形式如下:
#define WINAPI _stdcall
这是使编译器知道应当以Windows兼容方式产生机器指令。如果忽略它,程序仍然可以编译并运行,但在编译期间会收到一条警告,建议一定要确保WinMain函数具有WINAPI前缀。
hInstance:
第一个参数hInstance是一个句柄(Handle)。这是一个Windows在运行时(runtime)为程序提供了一个基本识别标志(ID)。Windows用这个句柄来识别程序,使它与同时处在运行状态的其他程序区别。
hPrevInstance:
第二个参数hPrevInstance也是一个句柄,但现在总是设为NULL。它曾被一些16位Windows应用程序使用,以达到同时打开一个程序的几个copy的目的。但现在已经没什么用处了。
lpCmdLine:
参数lpCmdLine是用来将命令行参数传递给应用程序的。LPSTR在WINNT.h中被定义为字符串指针。当从命令行运行应用程序时,字符串lpCmdLine中就会含有所输入的,除程序名字以外的所有字符。举例来说,如果执行程序的名称是MyGame.exe,而输入的命令是MyGame /s/d/log.txt,那么lpCmdLine中就会含有“/s/d/log.txt”字样。
nCmdShow:
最后一个参数nCmdShow告诉你的程序在启动后应怎样显示。有许多不同的参数可供选择,如下表所示。
表1.1 nCmdShow选项
参数 |
意义 |
SW_HIDE |
隐藏此窗口并激活另一个窗口 |
SW_MINIMIZE |
最小化指定窗口,并激活处在系统列表里最顶层的窗口 |
SW_RESTORE |
激活并显示某个窗口。如果该窗口处在最小化或最大化状态,则Windows替它恢复原始尺寸和位置 (注:与SW_SHOWNORMAL相同) |
SW_SHOW |
激活窗口并以当前尺寸和位置显示它 |
SW_SHOWMAXIMIZED |
激活窗口并以最大化方式显示 |
SW_SHOWMINIMIZED |
激活窗口并以图标方式显示 |
SW_SHOWMINNOACTIVE |
将某个窗口以图标方式显示。当前活动窗口不受影响 |
SW_SHOWNA |
将某个窗口以当前状态显示。当前活动窗口不受影响 |
SW_SHOWNOACTIVATE |
将某个窗口以前次尺寸和位置显示。当前活动窗口不受影响 |
SW_SHOWNORMAL |
激活并显示某个窗口。口如果该窗口处在最小化或最大化状态 (注:与SW_SHOWRESTORE作用相同) |
当用户在桌面或在开始菜单里创建应用程序的快捷方式时,他可以指定应用程序的打开方式。因此,如果用户觉得窗口运动后就应该马上最大化,那就把nCmdShow设成SW_SHOWMAXIMIZED就好了。
接下来看这一行代码:
MessageBox(NULL,"Hello World!","MsgBox",0);
它是成千个Win32 API函数之中的一个。它可以显示一个消息框,还可以通过一些参数来设置这个消息框的式样。特别是在需要向用户显示错误信息的时候,非常有用。
下面是它的函数原型:
int MessageBox(HWND hWnd, //拥有者(owner)窗体的句柄
LPCTSTR lpText, //消息框内显示的文本字符串
LPCTSTR lpCation, //消息框标题栏显示的文本字符串
UINT uType); //消息框的类型(显示几个按钮,什么样的图标提示等)
uType
表1.2消息框uType样式
按钮类型设置 |
|
标志(flag) |
意义 |
MB_ABORTRETRYIGNORE |
消息框带三个按钮:终止,重试,忽略 |
MB_OK |
消息框带一个按钮:确定(默认标志) |
MB_OKCANCEL |
消息框带二个按钮:确定和取消 |
MB_RETRYCANCEL |
消息框带二个按钮:重试和取消 |
MB_YESNO |
消息框带二个按钮:是和否 |
MB_YESNOCANCEL |
消息框带三个按钮:是,否,取消 |
图标类型设置 |
|
MB_ICONWARNING |
消息框中显示一个红色惊叹号图标 |
MB_ICONASTERISK |
消息框中显示一个蓝色小写字母i的图标 |
MB_ICONQUESTION |
消息框中显示一个黄色问号图标 |
MB_ICONSTOP |
消息框中显示一个红色STOP记号的图标(红叉) |
MessageBox还会给出一个返回值(HelloWorld1中忽略了这个返回值),它是意义是用户点击的按钮的代码。如果没有足够的内存来创建消息框,MessageBox将返回0.其他返回值如下:
MessageBox的返回值:
IDABORT 用户单击了终止按钮
IDCANCEL 用户单击了取消按钮
IDIGNORE 用户单击了忽略按钮
IDNO 用户单击了否按钮
IDOK 用户单击了确定按钮
IDRETRY 用户单击了重试按钮
IDYES 用户单击了是按钮
读者一定在疑惑那些变量名的奇怪前缀,如lp,sz和h等。微软程序员们在编程时都使用一种共同的约定——匈牙利符号表示法(Hungarian Notation)。下面介绍一下匈牙利表示法。
匈牙利表示法是微软雇员Charles Simonyi博士的发明。它之所以称为匈牙利表示法,是因Charles来自匈牙利。基本上,这是一个命名约定:在每一个变量名前添加表示变量类型的字母前缀,并继以一个大写字母开头的对变量的简短描述。例如,如果需要用一个整型变量来保存游戏中的得分,会把它命名为iScore。匈牙利表示法的发明来自于为微软程序员建立一个可遵循的编程规范的迫切需求。如果一个公司所有的程序员各自使用不同的命名约定,一切将变得非常混乱。
表1.3 匈牙利记号表示法前缀
前缀 |
类型 |
sz |
指向一个以/0字符结尾的字符串中的第一个字符 |
str |
字符串 |
i |
int |
n |
数或int |
ui |
unsigned int |
c |
char |
w |
字(无符号短整型) |
dw |
双字(无符号长整型) |
fn |
函数指针 |
d |
double |
by |
byte |
l |
long |
p |
指针 |
lp |
长整型指针 |
lpstr |
指向字符串的长整型指针 |
h |
句柄(handle) |
m_ |
类成员 |
g_ |
全局类型 |
hwnd |
窗口句柄 |
hdc |
Windows设备上下文(device context)的句柄 |
有两点需要说明:第一,如果在一个小函数里用到一个变量,那么笔者会使用他觉得适当的名字,并不严格遵循这一标准(默然:我非常赞同这样的观点,即:在99.99%的情况下,我们应该遵循规范,在0.01%的情况下,我们应该创新)。另外,笔者用大写字母C作为所有类的前缀,大写字母S作为所有结构的前缀。
在创建窗口之前,必须首先创建自己的窗口类并将它注册,这样操作系统才能知道希望此窗口以何种类型显示,并如何动作。窗口可以是任意尺寸,边界,滚动条和菜单这些都可有可无。
默然:我将整个HelloWorld2的代码在此列出,这样便于大家对照后面的说明。
默然:下面列出HelloWorld2程序的defines.h文件中源代码清单:
#ifndef DEFINES_H
#define DEFINES_H
#define WINDOW_WIDTH 400
#define WINDOW_HEIGHT 400
#endif
默然:下面列出HelloWorld2程序的main.cpp文件中源代码清单:
#include <windows.h>
#include "defines.h"
//-----------------------------------------------------------------------
//
// Name: HelloWorld2 示例工程
//
// Author: Mat Buckland 2002
// 修改及注释翻译:默然说话
// Desc: window窗口创建示例(这个窗口好象没什么用处)!
//
//------------------------------------------------------------------------
//--------------------------------- Globals ------------------------------
//
//------------------------------------------------------------------------
char* g_szApplicationName = "Hello World!";
char* g_szWindowClassName = "MyWindowClass";
//---------------------------- WindowProc ---------------------------------
//
// 这个回调函数处理所有的Windows消息。
// 我将在HelloWorld3中作详细讲解。
// 现在你只需要知道,这个示例要运行它必须存在。
//-------------------------------------------------------------------------
LRESULT CALLBACK WindowProc (HWND hwnd,
UINT msg,
WPARAM wParam,
LPARAM lParam)
{
return DefWindowProc (hwnd, msg, wParam, lParam);
}
//-------------------------------- WinMain -------------------------------
//
// Windows程序入口
//------------------------------------------------------------------------
int WINAPI WinMain (HINSTANCE hInstance,
HINSTANCE