了解API有助于了解Windows内部运行机制,因此在初学者接触MFC之前用API编写一个Windows程序是很有好处的.
我们用VC++6.0编写这个程序,首先打开VC++6.0打开File菜单,New命令建立一个Win32 Application类型的工程,注意,在建立工程时在第2步选择"A simple Win32 application.".将存放WinMain函数的文件修改如下:
//目的:演示利用Windows API开发Windows应用程序
//功能:鼠标键按下时,弹出消息框通知用户是按下了鼠标的哪个键
//*******************源代码如下****************************
#i nclude "stdafx.h"
INT PASCAL WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpszCmdParam,
INT nCmdShow);
LRESULT CALLBACK WndProc(HWND hMainwnd, //窗口句柄
UINT message, //消息标识
WPARAM wParam, //消息附加信息
LPARAM lParam); //消息附加信息
//以下定义全局变量
//实例句柄
HINSTANCE myhInst;
//应用程序名
char szAppName[]="WinAPIDemo";
//程序标题
char szAppTitle[]="利用应用程序接口的Windows应用程序";
INT PASCAL WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpszCmdParam,
INT nCmdShow);
{
//主窗口句柄(句柄为窗口windows中的标识)
HWND hMainwnd;
//定义windows消息数据结构
MSG message;
//定义窗口类
WNDCLASS myWC;
if(!hPrevInstance) //判断是否有其他应用程序实例在运行
{
//以下为窗口赋值
myWC.style =CS_HREDRAW|CS_VREDRAW;
myWC.lpfnWndProc=WndProc;
myWC.cbClsExtra=0;
myWC.cbWndExtra=0;
myWC.hInstance=hInstance;
myWC.hIcon=LoadIcon(NULL,IDI_APPLICATION);
myWC.hCursor=LoadCursor(NULL,IDC_ARROW);
myWC.hbrBackground=HBRUSH(GetStockObject(WHITE_BRUSH));
myWC.lpszMenuName=NULL;
myWC.lpszClassName=szAppName;
//注册窗口
RegisterClass(&myWC);
}
//创建应用程序窗口
hMainwnd=CreateWindow(szAppName, //窗口类名
szAppTitle, //窗口标题
WS_OVERLAPPEDWINDOW,//窗口风格
200, //初始X坐标
200, //初始Y坐标
400, //初始宽度
500, //初始高度
NULL, //父窗口句柄
NULL, //窗口菜单句柄
hInstance, //程序当前句柄
NULL //创建参数);
ShowWindow(hMainwnd,SW_SHOWMAXIMIZED); //显示窗口
UpdateWindow(hMainwnd); //刷新窗口用户区
while(GetMessage(&message,NULL,0,0)) //建立消息循环
{
TranslateMessage(&message); //翻译键盘消息
DispatchMessage(&message); //向消息处理模块发送消息
}
return message.wParam;
}
/***********************************************************************
Function:WndProc(HWND,UINT,WPARAM,LPARAM)窗口处理过程
Purpose :Processes message 处理消息
************************************************************************/
LRESULT CALLBACK WndProc(HWND hMainwnd, //窗口句柄
UINT message, //消息标识
WPARAM wParam, //消息附加信息
LPARAM lParam) //消息附加信息
{
char MsgLBTN[]="你刚按下了鼠标左键!";
char MsgRBTN[]="你刚按下了鼠标右键!";
//消息处理分支
switch(message)
{
case WM_RBUTTONDOWN: //右鼠标键被按下
{
MessageBeep(MB_ICONINFORMATION);
MessageBox(GetFocus(),MsgRBTN,"Message",MB_OK|MB_ICONINFORMATION);
break;
}
case WM_LBUTTONDOWN: //左鼠标键被按下
{
MessageBeep(MB_ICONINFORMATION);
MessageBox(GetFocus(),MsgLBTN,"Message",MB_OK|MB_ICONINFORMATION);
break;
}
case WM_DESTROY: //关闭窗口
{
PostQuitMessage(0);
return 0;
}
default: break;
}
//未列入处理的消息由windows缺省操作完成处理
return DefWindowProc(hMainwnd,message,wParam,lParam);
}
好了,程序完成运行一下,你可以看到如图所示的结果:
图5 API编写的Windows程序运行结果
运行时你可能会感到有些失望因为写了那么长的代码就这么简单的结果,的确是,用API写应用程序的确是很繁琐的事情,但是在其中很多地方可以反映Windows运行机制的内容.
对例程的剖析:
1.主函数WinMain():
(1)WinMain程序的入口:这就同DOS下的C程序中的main()函数类似,Windows程序的入口是WinMain().在WinMain这个主程序段中要完成注册窗口类型,创建窗口,显示窗口,建立消息循环等工作.
要注意的是:WinMain()前的PASCAL关键字,说明程序按照PASCAL调用规则调用函数.
(2)用RegisterClass函数注册窗口:任何窗口要在Windows中显示首先要注册,这样才能在Windows中管理.注册前要定义窗口类.窗口类是记录窗口信息的结构体在程序中WNDCLASSmyWC就是定义一个窗口类的数据结构.
myWC.style //定义窗口类型,风格
myWC.lpfnWndProc //定义窗口过程子过程程序名
myWC.hInstance //窗口当前句柄
myWC.hIcon //窗口用的图标
myWC.hCursor //定义窗口用的光标
myWC.hbrBackground //定义窗口背景
myWC.lpszMenuName //窗口菜单名,如果有的话将和资源定义程序一起使用
myWC.lpszClassName //定义该窗口类的名称
定义了窗口类后就可以注册窗口类了.用RegisterClass(&myWC)语句注册过后就可以建立窗口的实例.创建窗口类所用的 API函数CreateWindow,CreateWindow所用的参数包括了窗口实例的名字,标题,窗口风格,显示位置,窗口大小等.
现在为止,要创建的窗口已经在内存中准备好了,只等着显示出来.
(3)用ShowWindow函数将窗口显示出来.
(4)建立消息循环机制:消息循环是用While循环来实现的.
即:
while(GetMessage(&message,NULL,0,0)) //建立消息循环
{
TranslateMessage(&message); //翻译键盘消息
DispatchMessage(&message); //向消息处理模块发送消息
}
其中GetMessage函数从消息队列中取出一条消息,并将其放入Windows的消息结构MSG中.MSG结构的含义如下:
typedef struct tagMSG
{
HWND hwnd; //窗口句柄,Windows对窗口管理而负给每个窗口唯一的标识符.
UINT message; //消息值,是消息的标识符,在Windows.h头文件中定义.
WPARAM wParam; //消息附加参数,包含消息的内容.
LPARAM lParam; //消息附加参数,包含消息的内容.wParam,lParam是处理消息的重要依据.
DWORD time; //消息送入队的时间.
POINT pt; //消息发送时光标的屏幕坐标.
}MSG;
Windows程序将辨识消息的值UINT message/然后跳转到相应的处理程序.在处理程序中消息的wParam,lParam的内容决定具体如何处理消息.这就是事件驱动程序的实现机理.
TranslateMessage函数实现键盘消息翻译,将键盘消息翻译成Windows字符集.
DispatchMessage函数将消息发送到窗口处理.
到这里,WinMain主函数的任务已经完成,他创建了窗口和消息循环.
2.窗口过程---消息处理的主体
窗口过程函数是应用程序的主体部分,在这里将处理所有的消息,应用程序的各个功能就是在消息处理过程中实现的.
窗口过程的函数原型如下:
LRESULT CALLBACK WndProc(HWND hMainwnd, //窗口句柄
UINT message, //消息标识
WPARAM wParam, //消息附加信息
LPARAM lParam) //消息附加信息
这里的过程名WndProc已在RegisterClass注册窗口前的定义窗口类中出现过了,该过程名已同前面定义的窗口紧密的联系在一起.其后面的参数意义如下:
HWND hwnd //该参数是窗口过程对应的句柄
UINT message //该参数即MSG结构中的消息标识符
WPARAM wParam //MSG结构中的参数
LPARAM lParam //MSG结构中的参数
窗口处理程序往往很长,因为它要处理各种各样的消息.但窗口程序的主体结构很简单.就是switch-case结构,如下所示:
switch(message) //根据消息标识符,进入不同消息处理的分支
{
case WM_***: //WM_***代表一般的Windows的消息标识符
....... //此处为消息处理过程
break;
case WM_***:
switch(wParam) //根据不同的消息内容,进入不同的处理分支.
{
case ***: //***代表wParam的值,即消息的内容.
...... //此处加入根据消息内容进行的处理过程
break;
......
}
case WM_DESTROY: //WM_DESTROY是消息窗口的消息.
PostQuitMessage(0); //这条消息的处理是消息处理中必不可少的,否则窗口将无法关闭.
break;
default: //这样将处理未列入case结构的其他信息.
return Def Window Proc(hwnd,message,wParam,lParam)
//这里处理一般公共窗口消息,例如窗口的移动,大小的改变,最小化等
}
虽然窗口处理过程很简单,但使应用程序要处理的消息会很多,特别是应用程序由许多菜单和按钮,消息处理过程可能十分复杂.VC++的MFC的消息处理机制大大简化了这一过程,但我在这篇文章中还不涉及MFC的内容.
我们看到在例程中的switch-case结构中主要处理了两条消息:WM_LBUTTONDOWN和WM_RBUTTONDOWN.这两条消息是代表鼠标左键和右键按下的动作.当程序检测到鼠标动作时,转入相应的程序分支执行.
MessageBeep(MB_ICONINFORMATION);
这句程序是叫计算机发出声音.
MessageBox(GetFocus(),MsgLBTN,"Message",MB_OK|MB_ICONINFORMATION);
此句是弹出消息框,显示消息.