Windows程序设计基本概念
1. Windows运行机制-----消息驱动
消息驱动又叫做事件驱动,是Windows编程采用的程序设计思想。在这种程序结构中,程序没有明显的开始、结束,程序流程的控制由各种随机发生、不确定、没有预先设定顺序的事件的发生来触发。是一个不断产生消息和处理消息的过程。
也就是说程序一运行开始处于等待消息状态,取得消息以后,就对其做出相应进行处理,处理完以后又进入等待消息状态。这种程序结构与windows操作系统结合非常紧密,最明显一点就是消息的管理是由操作系统完成的。应用程序从操作系统获得消息的两种方式:一种就是应用程序调用windows提供的消息获取函数;另外一种就是回调函数,由操作系统自己调用。
1窗口:是Windows操作系统最重要最基本的一个概念。它是一个正在运行的应用程序相对应的矩形区域,通过它用户可以和应用程序进行交互。
2客户区:是窗口中最大的一块空白的矩形区域,是用户和系统进行交互的主要区域,一般用于显示应用程序的输出。
3标题栏:位于窗口顶部,用于显示应用程序名称的。
4菜单栏:位于标题栏下方,菜单栏列出了应用程序支持的大部分功能。 5图标:适用于提醒用户的一个小图像,代表一个应用程序。
6光标:Windows光标显示在屏幕上的一个小位图。
7工具栏:一般位于菜单栏下方,上面有一些位图按钮,代表一些常用功能。 8状态栏:位于这个窗口底端,用于输出菜单提示信息和一些其他详细信息。 9对话框:一种特殊的窗口,用于接受用户的输入输出。
10控件:对话框上的许多小窗口都是控件。如按钮,编辑框等都是控件。
2. Windows应用程序设计相关基本术语
1窗口:是应用程序操作的基本单元,是用户可以通过它和应用程序进行交互的接口环境,也是系统管理应用程序的基本单位。从程序运行的内存组织结构看,窗口对应一个数据结构WNDCLASS。
2实例:实际上实例就是一个可执行程序在内存中的拷贝。一个可执行程序运行多次,在内存中就有多个内存拷贝。系统是通过实例句柄来识别一个可执行程序的拷贝。
3句柄:系统用来识别不同对象或者同类对象的不同实例的“编号”。它是一个无符号整数。
几乎所有对对象的引用都是通过句柄来进行的。如使用HWND、HCURSOR、HDC。 4资源:构成应用程序的元素称为资源:菜单、工具条、位图、字符串等。
5窗口函数:用户通过窗口和应用程序交互时产生的消息,送给一个函数进行处理。该函数体结构大致由一个Switch结构组成,是消息驱动机制的发动机。
6图形设备接口:GDI(Graphic Device Interface)时Windows系统的重要组成部分。负责系统和用户或者绘图程序之间的信息交换,并控制输出设备上图形和文字的输出。最大的优点就是设备无关性:将程序员和设备相隔离,程序员不必关心物理设备的细节,直接调用相关的API函数就可以在输出设备上显示图形或者文字。
7回调函数:写好了等系统进行调用的函数。只能由系统自动调用。前面所说的窗口函数就是一个典型的回调函数。
3. Win32应用程序结构和执行原理
例 Win32应用程序结构分析和执行原理解释
使用Visual C++ 6.0建立一个简单的Win32应用程序:选择File菜单的New,在出现的对话框中,选择Projects栏目(新建工程),并点取其下的Win32 Application项,表示使用Win32环境创建应用程序。先在Locatin(路径)中填入"D:\TEST\",然后在Project Name(项目名称)中填入"MyWin",其它按照缺省设置)。单击OK按钮。
添加MyWin.cpp文件到工程,文件内容如下:
编译,运行该程序。运行结果为弹出一个简单的Windows窗口,提示:你好,我是简单的Win!点击鼠标右键,会谈出提示框:你按下了鼠标右键!
按下Esc键,会提示:你按下了ESC键!这是一个最简单的Win32应用程序,其他较复杂的Windows应用程序都是在这样的程序框架上扩展得到的。
下面对框架里边比较重要的函数进行解释说明。
1. WinMain()函数
WinMain()函数是应用程序开始执行时的入口点,通常也是应用程序结束任务退出时的出口点。它与DOS程序的main()函数起同样的作用,有一点不同的是,WinMain()函数必须带有四个参数,它们是系统传递给它的。WinMain()函数的原型如下:
int PASCAL WinMain( HINSTANCE hInstance, //当前实例句柄
HINSTANCE hPrevInstance, //前一个实例句柄
LPSTR lpCmdLine, int nCmdShow) //命令行字符 //窗口显示方式 第一个参数hInstance,是标识该应用程序当前的实例的句柄。它是HINSTANCE类型,HINSTANCE是Handle of Instance的缩写,表示实例的句柄。hInstance是一个很关键的数据,它唯一的代表该应用程序,在后面初始化程序主窗口的过程中需要用到这个 参数。这里有两个概念,一个是实例,一个是句柄。实例代表的是应用程序执行的整个过程和方法,一个应用程序如果没有被执行,只是存在 于磁盘上,那么就说它是没有被实例化的;只要一执行,则说该程序的一个实例在运行。句柄,顾名思义,指的是一个对象的把柄。在Windows中,有各种各 样的句柄,它们都是32位的指针变量,用来指向该对象所占据的内存区。句柄的使用,可以极大的方便Windows管理其内存中的各种对象。
第二个参数是hPrevInstance,它是用来标识该应用程序的前一个实例句柄。对于基于Win32的应用程序来说,这个参数总是NULL。这是因为在Win95操作系统中, 应用程序的每个实例都有各自独立的地址空间,即使同一个应用程序被执行了两次,在内存中也会为它们的每一个实例分配新的内存空间,所以一个应用程序被执行 后,不会有前一个实例存在的可能。也就是说,hPrevInstance这个参数是完全没有必要的,只是为了提供与16位Windows的应用程序形式上 的兼容性,才保留了这个参数。在以前的16位Windows环境下(如Windows3.2),hPrevInstance用来标识与hInstance 相关的应用程序的前一个句柄。 第三个参数是lpCmdLine,是指向应用程序命令行参数字符串的指针。如在Win95的"开始"菜单中单击"运行",输入"Mywin hello",则此参数指向的字符串为"hello"。
最后一个参数是nCmdShow,是一个用来指定窗口显示方式的整数。这个整数值可以是SW_SHOW、SW_HIDE、SW_SHOWMAXIMIZED、SW_SHOWMINIMIZED等。
2. 注册窗口类
上面的例程中,创建主窗口的函数是InitMyWindow()。通常要对填充一个窗口类结构WNDCLASS, 然后调用RegisterClass()对该窗口类进行注册。每个窗口都有一些基本的属性,如窗口边框、窗口标题栏文字、窗口大小和位置、鼠标、背景色、 处理窗口消息函数的名称等等。注册的过程也就是将这些属性告诉系统,然后再调用CreateWindow()函数创建出窗口。这好比人们量体裁衣。
typedef struct _WNDCLASS {
UINT style; //窗口的风格
WNDPROC lpfnWndProc; //指定窗口的消息处理函数的远指针
int cbClsExtra; //指定分配给窗口类结构之后的额外字节数
int cbWndExtra; //指定分配给窗口实例之后的额外字节数
HANDLE hInstance; //指定窗口过程所对应的实例句柄
HICON hIcon; //指定窗口的图标
HCURSOR hCursor; //指定窗口的鼠标
HBRUSH hbrBackground; //指定窗口的背景画刷
LPCTSTR lpszMenuName; //窗口的菜单资源名称
LPCTSTR lpszClassName; //该窗口类的名称
} WNDCLASS;
在Win95和WinNT的具有新界面特性的系统中,为了支持新的窗口界面特性,还有一种扩展的窗口类型WNDCLASSEX,它的定义如下:
typedef struct _WNDCLASSEX { UINT cbSize; //指定WNDCLASSEX结构的大小 UINT style; WNDPROC lpfnWndProc; int cbClsExtra; int cbWndExtra; HANDLE hInstance; HICON hIcon; HCURSOR hCursor; HBRUSH hbrBackground; LPCTSTR lpszMenuName; LPCTSTR lpszClassName; HICON hIconSm; //窗口的小图标 } WNDCLASSEX; 结构中相关成员这里进行简单解释一下,实际应用过程中查阅MSND。填充完毕后,调用RegisterClass函数进行窗口注册。函数原型如下:
ATOM RegisterClass( CONST WNDCLASS *lpWndClass ); ATOM RegisterClassEx( CONST WNDCLASSEX *lpwcx );
该函数如调用成功,则返回一个非0值,表明系统中已经注册了一个名为MyWin的窗口类。如果失败,则返回0。
3. 创建窗口
当窗口类注册完毕之后,并不会有窗口显示出来,因为注册的过程仅仅是为创建窗口所做的准备工作。实际创建一个窗口的是通过调用CreateWindow()函数完成的。窗口类中已经预先定义了窗口的一般属性,而CreateWindow()中的参数可以进一 步指定一个窗口的更具体的属性,在MyWin程序中,是如下调用CreateWindow()函数来创建窗口的:
hwnd = CreateWindow( "MyWin", //创建窗口所用的窗口类的名称 "一个简单的Win32程序", //窗口标题 WS_OVERLAPPEDWINDOW, //窗口风格,定义为普通型 200, //窗口位置的x坐标 200, //窗口位置的y坐标 500, //窗口的宽度 400, //窗口的高度 NULL, //父窗口句柄 NULL, //菜单句柄 hInstance, //应用程序实例句柄* NULL ); //一般都为NULL
如果窗口创建成功,返回值是新窗口的句柄,否则返回NULL。
显示和更新窗口
窗口创建后,并不会在屏幕上显示出来,要真正把窗口显示在屏幕上,还得使用ShowWindow()函数,其原型如下:
BOOL ShowWindow( HWND hWnd, int nCmdShow ); 参数hWnd指定要显示得窗口的句柄,nCmdShow表示窗口的显示方式,这里指定为从WinMain()函数的nCmdShow所传递而来的值。
由于ShowWindow()函数的执行优先级不高,所以当系统正忙着执行其它的任务时,窗口不会立即显示出来,此时,调用UpdateWindow()函数以可以立即显示窗口。其函数原型如下:
BOOL UpdateWindow( HWND hWnd );
4. 消息循环
在Win32编程中,消息循环是相当重要的一个概念,看似很难,但是使用起来却是非常简单。在WinMain()函数中,调用InitWindow()函数成功的创建了应用程序主窗口之后,就要启动消息循环,其代码如下:
while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); }
Windows应用程序可以接收以各种形式输入的信息,这包括键盘、鼠标动作 、记时器产生的消息,也可以是其它应用程序发来的消息等等。Windows系统自动监控所有的输入设备,并将其消息放入该应用程序的消息队列中。Windows消息结构及分类在下一章节进行讲解。
GetMessage()函数还可以过滤消息,它的第二个参数是用来指定从哪个窗口的消息队列中获取消息,其它窗口的消息将被过滤掉。如果该参数为NULL,则GetMessage()从该应用程序线程的所有窗口的消息队列中获取消息。
第三个和第四个参数是用来过滤MSG结构中主消息值的,主消息值在wMsgFilterMin和wMsgFilterMax之外的消息将被过滤掉。如果这两个参数为0,则表示接收所有消息。 当且仅当GetMessage()函数在获取到WM_QUIT消息后,将返回0值,于是程序退出消息循环。
TranslateMessage()函数的作用是把虚拟键消息转换到字符消息,以满足键盘输入的需要。DispatchMessage()函数所完成的工作是把当前的消息发送到对应的窗口过程中去。
5. 消息处理函数
消息处理函数又叫窗口过程,在这个函数中,不同的消息将用switch语句分配到不同的处理程序中 去。Windows的消息处理函数都有一个确定的样式,即这种函数的参数个数和类型以及其返回值的类型都有明确的规定。在VC的说明书中,消息处理函数的 原型是这样定义的:
LRESULT CALLBACK WindowProc(
HWND hwnd, //接收消息窗口的句柄 UINT uMsg, //主消息值 WPARAM wParam, //副消息值 LPARAM lParam //副消息值 ); 如果你的程序中还有其它的消息处理函数,也都必须按照上面的这个样式来定义,但函数名称可以随便取。MyWin中的WinProc()函数就是这样一个典型的消息处理函数。
消息处理函数的四个参数是由GetMessage()函数从消息队列中获得MSG结构,然后分解后得到的。第二个参数uMsg和MSG结构中的message值是一致的,代表了主消息值。程序中用switch语句来将不同类型的消息分配到不同的处理程序中去。
WinProc()函数明确的处理了4个消息,分别是WM_KEYDOWN(击键消息)、WM_RBUTTONDOWN(鼠标右键按下消息)、WM_PAINT(窗口重画消息)、WM_DESTROY(销毁窗口消息)。
值得注意的是,应用程序发送到窗口的消息远远不止以上这几条,象WM_SIZE、WM_MINIMIZE、WM_CREATE、WM_MOVE等这样频 频使用的消息就有几十条。为了减轻编程的负担,Windows的API提供了DefWindowProc()函数来处理这些最常用的消息,调用了这个函数后,这些消息将按照系统默认的方式得到处理。
因此,在switch_case语句中,只须明确的处理那些有必要进行特别响应的消息,把其余的消息交给DefWindowProc()函数来处理,是一种明智的选择,也是你必须做的一件事。
6. 结束消息循环
当用户按Alt+F4或单击窗口右上角的退出按钮,系统就向应用程序发送一条WM_DESTROY的消息。在处理此消息时,调用了 PostQuitMessage()函数,该函数会给窗口的消息队列中发送一条WM_QUIT的消息。在消息循环中,GetMessage()函数一旦检 索到这条消息,就会返回FALSE,从而结束消息循环,随后,程序也结束。
4. Windows应用程序编程接口
Windows操作系统对外通过两种接口提供服务:普通用户操作接口服务和程序员接口服务。其中用户操作接口服务是通过用户操作来进行完成的,而程序员接口服务是提供一系列底层的实现操作系统最基本的函数(接口)共程序员调度来完成相关功能。比如一个文件的拷贝,
普通用户可以通过鼠标菜单选择操作或者命令来完成,程序员可以通过程序代码调用相关文件操作的函数(API)来完成相同的功能。
所有这些系统底层、实现最基本功能、供程序员调用的函数集中起来形成一个集合,这就是API(Application Programming Interface)。Windows API编程是开发Windows应用程序最古老由最原始,同时又是试验一个程序员对windows操作系统及相关数据结构掌握程度的最好方法。
应该知道:所有其它类库以及后面要讲到的MFC,都是对这些最基本功能的函数进行封装来实现的。任何使用MFC能实现的功能,使用API同样能够实现,而且能够更加灵活。了解并学习API编程,可以看到使用MFC编程所看不到的东西,更深入了解Windows系统运行机制,同时帮助我们学习MFC编程。
三个动态连接库:Windows内核库(Kernel32.dll)、Windows用户界面管理库(User32.dll)、Windows图形设备界面库(Gdi32.dll),这些动态连接库共同构成了Win32 API函数。 Windows内核库(Kernel32.dll):所有底层的核心功能如任务管理、内存管理,进程线程文件管理等的函数都在这个动态连接库中;
Windows用户界面管理库(User32.dll):窗口管理,菜单管理以及通信等相关函数都在该动态连接库中;
Windows图形设备界面库(Gdi32.dll):集合了所有用于管理系统支持的所有图形设备函数。所有这三个动态连接库中的函数原型说明都在头文件windows.h中声明,所以在使用API进行编程序的时候别忘了包括该头文件。当然了,这只是主要的三个动态连接库,其他比较常用的还有网络服务(Winsock32.dll)、多媒体服务(Winmm.dll)等等。
两个例子程序分析:
1.文件拷贝
#include
#include
void main()
{
char SourceFileName[MAX_PATH];
char DestFileName[MAX_PATH];
BOOL Success;
cout
cin>>SourceFileName;
cout
cin>>DestFileName;
Success=CopyFile(SourceFileName,DestFileName,TRUE);
if(!Success)
cout
else
cout
}
2.网络相关
#include
#include
#include
void main()
{
// 定义数据结构存放计算机名称信息
DWORD MaxComputerlenth =MAX_COMPUTERNAME_LENGTH;
CHAR ComputerName[MAX_COMPUTERNAME_LENGTH];
// 调用API函数GetComputerName获取计算机名称存放到ComputerName中。 GetComputerName(ComputerName,&MaxComputerlenth);
cout
// 定义数据结构存放计算机系统信息
SYSTEM_INFO siSysInfo;
TCHAR tchBuffer[100];
// 调用API函数GetSystemInfo获取计算机名称存放到siSysInfo中
GetSystemInfo(&siSysInfo);
// 输出相关系统信息
sprintf(tchBuffer,"CPU的数量:%u\tCPU类型:%u",siSysInfo.dwNumberOfProcessors,siSysInfo.dwProcessorType);
cout
// 定义数据结构存放内存状态信息
MEMORYSTATUS stat;
// 调用API函数GetSystemInfo获取计算机名称存放到siSysInfo中
GlobalMemoryStatus(&stat);
// 进行输出
printf("%d%% 的内存在使用\n",stat.dwMemoryLoad);
printf("总共有%8ldk的物理内存空间. \n",stat.dwTotalPhys/1024);
printf("总共有%8ldk空闲的物理内存空间. \n",stat.dwAvailPhys/1024);
}
5. Windows 32 应用程序结构分析:
// Hello.cpp : Defines the entry point for the application.
//
#include "stdafx.h"
#include "resource.h"
#define MAX_LOADSTRING 100
// Global Variables:
HINSTANCE hInst; // current instance
TCHAR szTitle[MAX_LOADSTRING]; // The title bar text
TCHAR szWindowClass[MAX_LOADSTRING];
// The title bar text
// Foward declarations of functions included in this code module:
ATOM
BOOL MyRegisterClass(HINSTANCE hInstance); InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK About(HWND, UINT, WPARAM, LPARAM);
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
hAccelTable = LoadAccelerators(hInstance, (LPCTSTR)IDC_HELLO); // Perform application initialization: if (!InitInstance (hInstance, nCmdShow)) { } return FALSE; // Initialize global strings LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); LoadString(hInstance, IDC_HELLO, szWindowClass, MAX_LOADSTRING); MyRegisterClass(hInstance); // TODO: Place code here. MSG msg; HACCEL hAccelTable;
}
// while (GetMessage(&msg, NULL, 0, 0)) { } if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) { } TranslateMessage(&msg); DispatchMessage(&msg); return msg.wParam;
// FUNCTION: MyRegisterClass()
//
// PURPOSE: Registers the window class.
//
// COMMENTS:
//
// This function and its usage is only necessary if you want this code
// to be compatible with Win32 systems prior to the 'RegisterClassEx'
// function that was added to Windows 95. It is important to call this function
// so that the application will get 'well formed' small icons associated
// with it.
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
}
// wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = (WNDPROC)WndProc; wcex.cbClsExtra wcex.cbWndExtra wcex.hInstance wcex.hIcon = 0; = 0; = hInstance; = LoadIcon(hInstance, (LPCTSTR)IDI_HELLO); wcex.hCursor = LoadCursor(NULL, IDC_ARROW); = (HBRUSH)(COLOR_WINDOW+1); = (LPCSTR)IDC_HELLO; = szWindowClass; wcex.hbrBackground wcex.lpszMenuName wcex.lpszClassName wcex.hIconSm = LoadIcon(wcex.hInstance, (LPCTSTR)IDI_SMALL); return RegisterClassEx(&wcex);
// FUNCTION: InitInstance(HANDLE, int)
//
// PURPOSE: Saves instance handle and creates main window
//
// COMMENTS:
//
// In this function, we save the instance handle in a global variable and
// create and display the main program window.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
HWND hWnd;
hInst = hInstance; // Store instance handle in our global variable
hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
//
// FUNCTION: WndProc(HWND, unsigned, WORD, LONG)
//
// PURPOSE: Processes messages for the main window.
//
// WM_COMMAND - process the application menu
// WM_PAINT - Paint the main window
- post a quit message and return // WM_DESTROY
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
TCHAR szHello[MAX_LOADSTRING];
LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING);
switch (message)
{
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX,
(DLGPROC)About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code here...
RECT rt;
GetClientRect(hWnd, &rt);
DrawText(hdc, szHello, strlen(szHello), &rt, DT_CENTER); hWnd,
EndPaint(hWnd, &ps); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
// Mesage handler for about box.
LRESULT CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
} case WM_COMMAND: if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) { } break; EndDialog(hDlg, LOWORD(wParam)); return TRUE; switch (message) { case WM_INITDIALOG: return TRUE;
return FALSE;
}
Windows程序设计基本概念
1. Windows运行机制-----消息驱动
消息驱动又叫做事件驱动,是Windows编程采用的程序设计思想。在这种程序结构中,程序没有明显的开始、结束,程序流程的控制由各种随机发生、不确定、没有预先设定顺序的事件的发生来触发。是一个不断产生消息和处理消息的过程。
也就是说程序一运行开始处于等待消息状态,取得消息以后,就对其做出相应进行处理,处理完以后又进入等待消息状态。这种程序结构与windows操作系统结合非常紧密,最明显一点就是消息的管理是由操作系统完成的。应用程序从操作系统获得消息的两种方式:一种就是应用程序调用windows提供的消息获取函数;另外一种就是回调函数,由操作系统自己调用。
1窗口:是Windows操作系统最重要最基本的一个概念。它是一个正在运行的应用程序相对应的矩形区域,通过它用户可以和应用程序进行交互。
2客户区:是窗口中最大的一块空白的矩形区域,是用户和系统进行交互的主要区域,一般用于显示应用程序的输出。
3标题栏:位于窗口顶部,用于显示应用程序名称的。
4菜单栏:位于标题栏下方,菜单栏列出了应用程序支持的大部分功能。 5图标:适用于提醒用户的一个小图像,代表一个应用程序。
6光标:Windows光标显示在屏幕上的一个小位图。
7工具栏:一般位于菜单栏下方,上面有一些位图按钮,代表一些常用功能。 8状态栏:位于这个窗口底端,用于输出菜单提示信息和一些其他详细信息。 9对话框:一种特殊的窗口,用于接受用户的输入输出。
10控件:对话框上的许多小窗口都是控件。如按钮,编辑框等都是控件。
2. Windows应用程序设计相关基本术语
1窗口:是应用程序操作的基本单元,是用户可以通过它和应用程序进行交互的接口环境,也是系统管理应用程序的基本单位。从程序运行的内存组织结构看,窗口对应一个数据结构WNDCLASS。
2实例:实际上实例就是一个可执行程序在内存中的拷贝。一个可执行程序运行多次,在内存中就有多个内存拷贝。系统是通过实例句柄来识别一个可执行程序的拷贝。
3句柄:系统用来识别不同对象或者同类对象的不同实例的“编号”。它是一个无符号整数。
几乎所有对对象的引用都是通过句柄来进行的。如使用HWND、HCURSOR、HDC。 4资源:构成应用程序的元素称为资源:菜单、工具条、位图、字符串等。
5窗口函数:用户通过窗口和应用程序交互时产生的消息,送给一个函数进行处理。该函数体结构大致由一个Switch结构组成,是消息驱动机制的发动机。
6图形设备接口:GDI(Graphic Device Interface)时Windows系统的重要组成部分。负责系统和用户或者绘图程序之间的信息交换,并控制输出设备上图形和文字的输出。最大的优点就是设备无关性:将程序员和设备相隔离,程序员不必关心物理设备的细节,直接调用相关的API函数就可以在输出设备上显示图形或者文字。
7回调函数:写好了等系统进行调用的函数。只能由系统自动调用。前面所说的窗口函数就是一个典型的回调函数。
3. Win32应用程序结构和执行原理
例 Win32应用程序结构分析和执行原理解释
使用Visual C++ 6.0建立一个简单的Win32应用程序:选择File菜单的New,在出现的对话框中,选择Projects栏目(新建工程),并点取其下的Win32 Application项,表示使用Win32环境创建应用程序。先在Locatin(路径)中填入"D:\TEST\",然后在Project Name(项目名称)中填入"MyWin",其它按照缺省设置)。单击OK按钮。
添加MyWin.cpp文件到工程,文件内容如下:
编译,运行该程序。运行结果为弹出一个简单的Windows窗口,提示:你好,我是简单的Win!点击鼠标右键,会谈出提示框:你按下了鼠标右键!
按下Esc键,会提示:你按下了ESC键!这是一个最简单的Win32应用程序,其他较复杂的Windows应用程序都是在这样的程序框架上扩展得到的。
下面对框架里边比较重要的函数进行解释说明。
1. WinMain()函数
WinMain()函数是应用程序开始执行时的入口点,通常也是应用程序结束任务退出时的出口点。它与DOS程序的main()函数起同样的作用,有一点不同的是,WinMain()函数必须带有四个参数,它们是系统传递给它的。WinMain()函数的原型如下:
int PASCAL WinMain( HINSTANCE hInstance, //当前实例句柄
HINSTANCE hPrevInstance, //前一个实例句柄
LPSTR lpCmdLine, int nCmdShow) //命令行字符 //窗口显示方式 第一个参数hInstance,是标识该应用程序当前的实例的句柄。它是HINSTANCE类型,HINSTANCE是Handle of Instance的缩写,表示实例的句柄。hInstance是一个很关键的数据,它唯一的代表该应用程序,在后面初始化程序主窗口的过程中需要用到这个 参数。这里有两个概念,一个是实例,一个是句柄。实例代表的是应用程序执行的整个过程和方法,一个应用程序如果没有被执行,只是存在 于磁盘上,那么就说它是没有被实例化的;只要一执行,则说该程序的一个实例在运行。句柄,顾名思义,指的是一个对象的把柄。在Windows中,有各种各 样的句柄,它们都是32位的指针变量,用来指向该对象所占据的内存区。句柄的使用,可以极大的方便Windows管理其内存中的各种对象。
第二个参数是hPrevInstance,它是用来标识该应用程序的前一个实例句柄。对于基于Win32的应用程序来说,这个参数总是NULL。这是因为在Win95操作系统中, 应用程序的每个实例都有各自独立的地址空间,即使同一个应用程序被执行了两次,在内存中也会为它们的每一个实例分配新的内存空间,所以一个应用程序被执行 后,不会有前一个实例存在的可能。也就是说,hPrevInstance这个参数是完全没有必要的,只是为了提供与16位Windows的应用程序形式上 的兼容性,才保留了这个参数。在以前的16位Windows环境下(如Windows3.2),hPrevInstance用来标识与hInstance 相关的应用程序的前一个句柄。 第三个参数是lpCmdLine,是指向应用程序命令行参数字符串的指针。如在Win95的"开始"菜单中单击"运行",输入"Mywin hello",则此参数指向的字符串为"hello"。
最后一个参数是nCmdShow,是一个用来指定窗口显示方式的整数。这个整数值可以是SW_SHOW、SW_HIDE、SW_SHOWMAXIMIZED、SW_SHOWMINIMIZED等。
2. 注册窗口类
上面的例程中,创建主窗口的函数是InitMyWindow()。通常要对填充一个窗口类结构WNDCLASS, 然后调用RegisterClass()对该窗口类进行注册。每个窗口都有一些基本的属性,如窗口边框、窗口标题栏文字、窗口大小和位置、鼠标、背景色、 处理窗口消息函数的名称等等。注册的过程也就是将这些属性告诉系统,然后再调用CreateWindow()函数创建出窗口。这好比人们量体裁衣。
typedef struct _WNDCLASS {
UINT style; //窗口的风格
WNDPROC lpfnWndProc; //指定窗口的消息处理函数的远指针
int cbClsExtra; //指定分配给窗口类结构之后的额外字节数
int cbWndExtra; //指定分配给窗口实例之后的额外字节数
HANDLE hInstance; //指定窗口过程所对应的实例句柄
HICON hIcon; //指定窗口的图标
HCURSOR hCursor; //指定窗口的鼠标
HBRUSH hbrBackground; //指定窗口的背景画刷
LPCTSTR lpszMenuName; //窗口的菜单资源名称
LPCTSTR lpszClassName; //该窗口类的名称
} WNDCLASS;
在Win95和WinNT的具有新界面特性的系统中,为了支持新的窗口界面特性,还有一种扩展的窗口类型WNDCLASSEX,它的定义如下:
typedef struct _WNDCLASSEX { UINT cbSize; //指定WNDCLASSEX结构的大小 UINT style; WNDPROC lpfnWndProc; int cbClsExtra; int cbWndExtra; HANDLE hInstance; HICON hIcon; HCURSOR hCursor; HBRUSH hbrBackground; LPCTSTR lpszMenuName; LPCTSTR lpszClassName; HICON hIconSm; //窗口的小图标 } WNDCLASSEX; 结构中相关成员这里进行简单解释一下,实际应用过程中查阅MSND。填充完毕后,调用RegisterClass函数进行窗口注册。函数原型如下:
ATOM RegisterClass( CONST WNDCLASS *lpWndClass ); ATOM RegisterClassEx( CONST WNDCLASSEX *lpwcx );
该函数如调用成功,则返回一个非0值,表明系统中已经注册了一个名为MyWin的窗口类。如果失败,则返回0。
3. 创建窗口
当窗口类注册完毕之后,并不会有窗口显示出来,因为注册的过程仅仅是为创建窗口所做的准备工作。实际创建一个窗口的是通过调用CreateWindow()函数完成的。窗口类中已经预先定义了窗口的一般属性,而CreateWindow()中的参数可以进一 步指定一个窗口的更具体的属性,在MyWin程序中,是如下调用CreateWindow()函数来创建窗口的:
hwnd = CreateWindow( "MyWin", //创建窗口所用的窗口类的名称 "一个简单的Win32程序", //窗口标题 WS_OVERLAPPEDWINDOW, //窗口风格,定义为普通型 200, //窗口位置的x坐标 200, //窗口位置的y坐标 500, //窗口的宽度 400, //窗口的高度 NULL, //父窗口句柄 NULL, //菜单句柄 hInstance, //应用程序实例句柄* NULL ); //一般都为NULL
如果窗口创建成功,返回值是新窗口的句柄,否则返回NULL。
显示和更新窗口
窗口创建后,并不会在屏幕上显示出来,要真正把窗口显示在屏幕上,还得使用ShowWindow()函数,其原型如下:
BOOL ShowWindow( HWND hWnd, int nCmdShow ); 参数hWnd指定要显示得窗口的句柄,nCmdShow表示窗口的显示方式,这里指定为从WinMain()函数的nCmdShow所传递而来的值。
由于ShowWindow()函数的执行优先级不高,所以当系统正忙着执行其它的任务时,窗口不会立即显示出来,此时,调用UpdateWindow()函数以可以立即显示窗口。其函数原型如下:
BOOL UpdateWindow( HWND hWnd );
4. 消息循环
在Win32编程中,消息循环是相当重要的一个概念,看似很难,但是使用起来却是非常简单。在WinMain()函数中,调用InitWindow()函数成功的创建了应用程序主窗口之后,就要启动消息循环,其代码如下:
while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); }
Windows应用程序可以接收以各种形式输入的信息,这包括键盘、鼠标动作 、记时器产生的消息,也可以是其它应用程序发来的消息等等。Windows系统自动监控所有的输入设备,并将其消息放入该应用程序的消息队列中。Windows消息结构及分类在下一章节进行讲解。
GetMessage()函数还可以过滤消息,它的第二个参数是用来指定从哪个窗口的消息队列中获取消息,其它窗口的消息将被过滤掉。如果该参数为NULL,则GetMessage()从该应用程序线程的所有窗口的消息队列中获取消息。
第三个和第四个参数是用来过滤MSG结构中主消息值的,主消息值在wMsgFilterMin和wMsgFilterMax之外的消息将被过滤掉。如果这两个参数为0,则表示接收所有消息。 当且仅当GetMessage()函数在获取到WM_QUIT消息后,将返回0值,于是程序退出消息循环。
TranslateMessage()函数的作用是把虚拟键消息转换到字符消息,以满足键盘输入的需要。DispatchMessage()函数所完成的工作是把当前的消息发送到对应的窗口过程中去。
5. 消息处理函数
消息处理函数又叫窗口过程,在这个函数中,不同的消息将用switch语句分配到不同的处理程序中 去。Windows的消息处理函数都有一个确定的样式,即这种函数的参数个数和类型以及其返回值的类型都有明确的规定。在VC的说明书中,消息处理函数的 原型是这样定义的:
LRESULT CALLBACK WindowProc(
HWND hwnd, //接收消息窗口的句柄 UINT uMsg, //主消息值 WPARAM wParam, //副消息值 LPARAM lParam //副消息值 ); 如果你的程序中还有其它的消息处理函数,也都必须按照上面的这个样式来定义,但函数名称可以随便取。MyWin中的WinProc()函数就是这样一个典型的消息处理函数。
消息处理函数的四个参数是由GetMessage()函数从消息队列中获得MSG结构,然后分解后得到的。第二个参数uMsg和MSG结构中的message值是一致的,代表了主消息值。程序中用switch语句来将不同类型的消息分配到不同的处理程序中去。
WinProc()函数明确的处理了4个消息,分别是WM_KEYDOWN(击键消息)、WM_RBUTTONDOWN(鼠标右键按下消息)、WM_PAINT(窗口重画消息)、WM_DESTROY(销毁窗口消息)。
值得注意的是,应用程序发送到窗口的消息远远不止以上这几条,象WM_SIZE、WM_MINIMIZE、WM_CREATE、WM_MOVE等这样频 频使用的消息就有几十条。为了减轻编程的负担,Windows的API提供了DefWindowProc()函数来处理这些最常用的消息,调用了这个函数后,这些消息将按照系统默认的方式得到处理。
因此,在switch_case语句中,只须明确的处理那些有必要进行特别响应的消息,把其余的消息交给DefWindowProc()函数来处理,是一种明智的选择,也是你必须做的一件事。
6. 结束消息循环
当用户按Alt+F4或单击窗口右上角的退出按钮,系统就向应用程序发送一条WM_DESTROY的消息。在处理此消息时,调用了 PostQuitMessage()函数,该函数会给窗口的消息队列中发送一条WM_QUIT的消息。在消息循环中,GetMessage()函数一旦检 索到这条消息,就会返回FALSE,从而结束消息循环,随后,程序也结束。
4. Windows应用程序编程接口
Windows操作系统对外通过两种接口提供服务:普通用户操作接口服务和程序员接口服务。其中用户操作接口服务是通过用户操作来进行完成的,而程序员接口服务是提供一系列底层的实现操作系统最基本的函数(接口)共程序员调度来完成相关功能。比如一个文件的拷贝,
普通用户可以通过鼠标菜单选择操作或者命令来完成,程序员可以通过程序代码调用相关文件操作的函数(API)来完成相同的功能。
所有这些系统底层、实现最基本功能、供程序员调用的函数集中起来形成一个集合,这就是API(Application Programming Interface)。Windows API编程是开发Windows应用程序最古老由最原始,同时又是试验一个程序员对windows操作系统及相关数据结构掌握程度的最好方法。
应该知道:所有其它类库以及后面要讲到的MFC,都是对这些最基本功能的函数进行封装来实现的。任何使用MFC能实现的功能,使用API同样能够实现,而且能够更加灵活。了解并学习API编程,可以看到使用MFC编程所看不到的东西,更深入了解Windows系统运行机制,同时帮助我们学习MFC编程。
三个动态连接库:Windows内核库(Kernel32.dll)、Windows用户界面管理库(User32.dll)、Windows图形设备界面库(Gdi32.dll),这些动态连接库共同构成了Win32 API函数。 Windows内核库(Kernel32.dll):所有底层的核心功能如任务管理、内存管理,进程线程文件管理等的函数都在这个动态连接库中;
Windows用户界面管理库(User32.dll):窗口管理,菜单管理以及通信等相关函数都在该动态连接库中;
Windows图形设备界面库(Gdi32.dll):集合了所有用于管理系统支持的所有图形设备函数。所有这三个动态连接库中的函数原型说明都在头文件windows.h中声明,所以在使用API进行编程序的时候别忘了包括该头文件。当然了,这只是主要的三个动态连接库,其他比较常用的还有网络服务(Winsock32.dll)、多媒体服务(Winmm.dll)等等。
两个例子程序分析:
1.文件拷贝
#include
#include
void main()
{
char SourceFileName[MAX_PATH];
char DestFileName[MAX_PATH];
BOOL Success;
cout
cin>>SourceFileName;
cout
cin>>DestFileName;
Success=CopyFile(SourceFileName,DestFileName,TRUE);
if(!Success)
cout
else
cout
}
2.网络相关
#include
#include
#include
void main()
{
// 定义数据结构存放计算机名称信息
DWORD MaxComputerlenth =MAX_COMPUTERNAME_LENGTH;
CHAR ComputerName[MAX_COMPUTERNAME_LENGTH];
// 调用API函数GetComputerName获取计算机名称存放到ComputerName中。 GetComputerName(ComputerName,&MaxComputerlenth);
cout
// 定义数据结构存放计算机系统信息
SYSTEM_INFO siSysInfo;
TCHAR tchBuffer[100];
// 调用API函数GetSystemInfo获取计算机名称存放到siSysInfo中
GetSystemInfo(&siSysInfo);
// 输出相关系统信息
sprintf(tchBuffer,"CPU的数量:%u\tCPU类型:%u",siSysInfo.dwNumberOfProcessors,siSysInfo.dwProcessorType);
cout
// 定义数据结构存放内存状态信息
MEMORYSTATUS stat;
// 调用API函数GetSystemInfo获取计算机名称存放到siSysInfo中
GlobalMemoryStatus(&stat);
// 进行输出
printf("%d%% 的内存在使用\n",stat.dwMemoryLoad);
printf("总共有%8ldk的物理内存空间. \n",stat.dwTotalPhys/1024);
printf("总共有%8ldk空闲的物理内存空间. \n",stat.dwAvailPhys/1024);
}
5. Windows 32 应用程序结构分析:
// Hello.cpp : Defines the entry point for the application.
//
#include "stdafx.h"
#include "resource.h"
#define MAX_LOADSTRING 100
// Global Variables:
HINSTANCE hInst; // current instance
TCHAR szTitle[MAX_LOADSTRING]; // The title bar text
TCHAR szWindowClass[MAX_LOADSTRING];
// The title bar text
// Foward declarations of functions included in this code module:
ATOM
BOOL MyRegisterClass(HINSTANCE hInstance); InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK About(HWND, UINT, WPARAM, LPARAM);
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
hAccelTable = LoadAccelerators(hInstance, (LPCTSTR)IDC_HELLO); // Perform application initialization: if (!InitInstance (hInstance, nCmdShow)) { } return FALSE; // Initialize global strings LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); LoadString(hInstance, IDC_HELLO, szWindowClass, MAX_LOADSTRING); MyRegisterClass(hInstance); // TODO: Place code here. MSG msg; HACCEL hAccelTable;
}
// while (GetMessage(&msg, NULL, 0, 0)) { } if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) { } TranslateMessage(&msg); DispatchMessage(&msg); return msg.wParam;
// FUNCTION: MyRegisterClass()
//
// PURPOSE: Registers the window class.
//
// COMMENTS:
//
// This function and its usage is only necessary if you want this code
// to be compatible with Win32 systems prior to the 'RegisterClassEx'
// function that was added to Windows 95. It is important to call this function
// so that the application will get 'well formed' small icons associated
// with it.
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
}
// wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = (WNDPROC)WndProc; wcex.cbClsExtra wcex.cbWndExtra wcex.hInstance wcex.hIcon = 0; = 0; = hInstance; = LoadIcon(hInstance, (LPCTSTR)IDI_HELLO); wcex.hCursor = LoadCursor(NULL, IDC_ARROW); = (HBRUSH)(COLOR_WINDOW+1); = (LPCSTR)IDC_HELLO; = szWindowClass; wcex.hbrBackground wcex.lpszMenuName wcex.lpszClassName wcex.hIconSm = LoadIcon(wcex.hInstance, (LPCTSTR)IDI_SMALL); return RegisterClassEx(&wcex);
// FUNCTION: InitInstance(HANDLE, int)
//
// PURPOSE: Saves instance handle and creates main window
//
// COMMENTS:
//
// In this function, we save the instance handle in a global variable and
// create and display the main program window.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
HWND hWnd;
hInst = hInstance; // Store instance handle in our global variable
hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
//
// FUNCTION: WndProc(HWND, unsigned, WORD, LONG)
//
// PURPOSE: Processes messages for the main window.
//
// WM_COMMAND - process the application menu
// WM_PAINT - Paint the main window
- post a quit message and return // WM_DESTROY
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
TCHAR szHello[MAX_LOADSTRING];
LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING);
switch (message)
{
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX,
(DLGPROC)About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code here...
RECT rt;
GetClientRect(hWnd, &rt);
DrawText(hdc, szHello, strlen(szHello), &rt, DT_CENTER); hWnd,
EndPaint(hWnd, &ps); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
// Mesage handler for about box.
LRESULT CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
} case WM_COMMAND: if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) { } break; EndDialog(hDlg, LOWORD(wParam)); return TRUE; switch (message) { case WM_INITDIALOG: return TRUE;
return FALSE;
}