系统相关
首页 > 系统相关> > Windows 编程机制与第一个程序

Windows 编程机制与第一个程序

作者:互联网

目录

Windows 程序工作原理

Windows 程序设计完全不同于 DOS 程序设计方法,采用的是基于事件驱动方式的程序设计模式。Windows 系统是通过事件驱动的,事件驱动也就是程序的事件是围绕着消息的产生与处理展开,一条消息是关于发生的事件的消息,事件驱动是靠消息循环机制来实现的。
在 Windows 编程中,所有的程序都是一个个窗口。在整个系统中,使用窗口句柄唯一标识一个窗口,每个窗口都有自己的窗口过程来处理消息。当 Windows 程序开始运行时,首先先初始化程序,然后初始化并创建一个窗口。窗口进入消息循环等待消息的到来,接收到消息的时候先看看是不是退出程序,如果是就终止程序。如果不是则判断是否是自己感兴趣的消息,如果是就进行相应的响应和处理。对于窗口不感兴趣的消息,则使用默认的消息处理过程。

Windows 为每一个应用程序维护相应的消息队列,应用程序的任务就是处理消息循环。16 位的操作系统中只有一个消息队列,所以系统必须等待当前任务处理消息后才可以发送下一消息到相应程序。32 位的系统中每一个运行的程序都会有一个消息队列,所以系统可以在多个消息队列中转换。Windows 应用程序的消息来源有输入消息、控制消息、系统消息、用户消息 4 种,这些消息会被窗口绑定的回调函数(Cal lback Function)处理。

第一个 Windows 程序

运行 Windows 应用程序在桌面显示 Windows 窗口,且窗口中居中显示“大家好,这是我的第一个 Windows API 程序!”同时播放背景音乐。一个简单的 Windows API 程序由 2 部分组成,分别是 WinMain 函数CALLBACK 函数,Windows 程序以 WinMain 函数作为进入程序的初始人口点。Windows 系统是通过事件驱动的,事件驱动围绕着消息的产生与处理展开,一条消息是关于发生的事件的消息。CALLBACK 函数就是当窗口接收到消息时,负责对对应的消息做出动作和响应。

WinMain 函数

WinMain 函数的原型为 int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR IpCmdLine, int nCmdshow),一般情况下我们应该在 WinMain 函数中完成下面的操作:

首先先解释一下 Main 函数的各个参数的含义:

参数 说明
hinstance 应用程序当前实例的句柄
hPrevlnstance 应用程序的先前实例的句柄
szCmdLine 指向应用程序命令行的指针
iCmdShow 指明窗口如何显示

实例化窗口类

接着初始化一些变量,例如窗口名、消息句柄和消息:

static TCHAR szAppName[] = TEXT("HelloWorld!");
HWND hwnd;
MSG msg;

接着需要实例化窗口类,并且设置窗口的一些基本属性,包括窗口的样式、图标、背景和鼠标样式等,还要绑定 callback 函数。

WNDCLASS wndclass;    //WNDCLASS是一个由系统支持的结构,用来储存某一类窗口的信息
wndclass.style = CS_HREDRAW | CS_VREDRAW;    // 窗口类的风格
wndclass.lpfnWndProc = WndProc;    //窗口处理的回调函数
wndclass.cbClsExtra = 0;    //窗口扩展
wndclass.cbWndExtra = 0;    //窗口实例扩展
wndclass.hInstance = hInstance;    //实例句柄
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);    //窗口的图标
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);   //窗口鼠标光标
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);   //窗口背景色
wndclass.lpszMenuName = NULL;   //窗口菜单

注册窗口

实例化窗口类后,我们需要将这个窗口对象注册,只有注册好的窗口对象才能够被显示。注册窗口需要使用 RegisterClass(&wndclass) 方法,注册成功返回 true,此时使用一个 if 判断一下是否注册成功,注册失败显示提示信息并返回。

wndclass.lpszClassName = szAppName;   //窗口类名
if(!RegisterClass(&wndclass))    //注册窗口
{
	//显示一个模态对话框
	MessageBox(NULL,TEXT("This program RegisterClass is error!"), szAppName, MB_ICONERROR);
	return 0;
}

显示并更新窗口

接下来需要在句柄中设置窗口显示的相关信息,并利用该句柄和 ShowWindow(hwnd, iCmdShow) 方法显示窗口,显示后用 UpdateWindow(hwnd) 更新窗口完成显示。

hwnd = CreateWindow(szAppName,                    //lpClassName:窗口类名 
                    TEXT("The Hello Program"),    //lpWindowName:窗口标题 
                    WS_OVERLAPPEDWINDOW,          //dwStyle:指定创建窗口的风格
                    CW_USEDEFAULT,                //X:指定窗口的初始水平位置
                    CW_USEDEFAULT,                //Y:指定窗口的初始垂直位置
                    CW_USEDEFAULT,                //nWidth:以设备单元指明窗口的宽度
                    CW_USEDEFAULT,                //nHeight:以设备单元指明窗口的高度
                    NULL,                         //hWndParent:指向被创建窗口的父窗口或所有者窗口的句柄
                    NULL,                         //hMenu:菜单句柄 
                    hInstance,                    //hlnstance:与窗口相关联的模块实例的句柄
                    NULL);                        //lpParam:该值传递给窗口WM_CREATE消息
ShowWindow(hwnd, iCmdShow);    //该函数设置指定窗口的显示状态
UpdateWindow(hwnd);    //更新指定窗口的客户区

消息循环

窗口显示在桌面后,就需要循环侦听传来的消息,转换成相应的消息标识后做相应的处理即可。

while(GetMessage(&msg, NULL, 0, 0))    //从调用线程的消息队列里取得一个消息并将其放于指定的结构
{ 
	TranslateMessage(&msg);    //用于将虚拟键消息转换为字符消息
	DispatchMessage(&msg);    //函数分发一个消息给窗口程序
} 
return msg.wParam;

CALLBACK 函数

回调函数并不由开发者直接调用执行,只是使用系统接口 API 函数作为起点,回调函数通常作为参数传递给系统 API,由该 API 来调用。回调函数可能被系统 API 调用一次,也可能被循环调用多次。此处就是当窗口接收到一个消息时,将调用 CALLBACK 函数对消息进行处理。代码如下:

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM IParam)
{ 
	HDC hdc;    //HDC:设备场景句柄
	PAINTSTRUCT ps;    //PAINTSTRUCT:绘图信息结构
	RECT rect;    //rect:存储成对出现的参数,比如一个矩形框的左上角坐标、宽度和高度
	switch(message)
	{
		case WM_CREATE:
			PlaySound(TEXT("hellowin.wav"), NULL, SND_FILENAME|SND_ASYNC);    //播放音频
			return 0;
		case WM_PAINT:
			hdc = BeginPaint(hwnd, &ps);    //为指定窗口进行绘图工作的准备
			GetClientRect(hwnd, &rect);    //函数获取窗口客户区的大小
			//在指定的矩形里写入格式化的正文
			DrawText(hdc, TEXT("大家好,这是我的第一个Windows API 程序!"), -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
			EndPaint(hwnd, &ps);
			return 0;
		case WM_DESTROY:    //该函数向系统表明有个线程有终止请求
			PostQuitMessage(0);
			return 0;
	}
	return DefWindowProc(hwnd, message, wParam, IParam);    //调用缺省的窗口过程来为应用程序没有处理的任何窗口消息提供缺省的处理
}

DEV-C++ 设置

如果使用 DEV-C++ 来编译 Windows 程序,需要在编译时加入“-mwindows -lwinmm”命令,首先先打开工具中的“编译选项”。

选中“编译时加入以下命令”,并且写上“-mwindows -lwinmm”命令,保存并编译。

运行效果

完整代码

#include <windows.h>
//LRESULT就是long,CALLBACK是回调函数,参数分别是窗口句柄,消息,消息参数,消息参数
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
/*
hinstance:应用程序当前实例的句柄
hPrevlnstance:应用程序的先前实例的句柄
szCmdLine:指向应用程序命令行的指针
iCmdShow:指明窗口如何显示
*/
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
	static TCHAR szAppName[] = TEXT("HelloWorld!");
	HWND hwnd;
	MSG msg;
	WNDCLASS wndclass;    //WNDCLASS是一个由系统支持的结构,用来储存某一类窗口的信息
	wndclass.style = CS_HREDRAW | CS_VREDRAW;    // 窗口类的风格
	wndclass.lpfnWndProc = WndProc;    //窗口处理的回调函数
	wndclass.cbClsExtra = 0;    //窗口扩展
	wndclass.cbWndExtra = 0;    //窗口实例扩展
	wndclass.hInstance = hInstance;    //实例句柄
	wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);    //窗口的图标
	wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);   //窗口鼠标光标
	wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);   //窗口背景色
	/*wndclass.hCursor = LoadCursor(NULL, IDC_HAND);//鼠标图案为手指
    wndclass.hbrBackground = (HBRUSH)(COLOR_WINDOW+2); //窗口背景为灰色*/
    wndclass.lpszMenuName = NULL;   //窗口菜单

	wndclass.lpszClassName = szAppName;   //窗口类名
	if(!RegisterClass(&wndclass))
	{
		//显示一个模态对话框
		MessageBox(NULL,TEXT("This program RegisterClass is error!"), szAppName, MB_ICONERROR);
		return 0;
	}
	
	hwnd = CreateWindow(szAppName,                    //lpClassName:窗口类名 
						TEXT("The Hello Program"),    //lpWindowName:窗口标题 
						//WS_POPUP | WS_BORDER | WS_THICKFRAME, 
						WS_OVERLAPPEDWINDOW,          //dwStyle:指定创建窗口的风格
						CW_USEDEFAULT,                //X:指定窗口的初始水平位置
						CW_USEDEFAULT,                //Y:指定窗口的初始垂直位置
						CW_USEDEFAULT,                //nWidth:以设备单元指明窗口的宽度
						CW_USEDEFAULT,                //nHeight:以设备单元指明窗口的高度
						NULL,                         //hWndParent:指向被创建窗口的父窗口或所有者窗口的句柄
						NULL,                         //hMenu:菜单句柄 
						hInstance,                    //hlnstance:与窗口相关联的模块实例的句柄
						NULL);                        //lpParam:该值传递给窗口WM_CREATE消息
	ShowWindow(hwnd, iCmdShow);    //该函数设置指定窗口的显示状态
	UpdateWindow(hwnd);    //更新指定窗口的客户区
	while(GetMessage(&msg, NULL, 0, 0))    //从调用线程的消息队列里取得一个消息并将其放于指定的结构
	{ 
		TranslateMessage(&msg);    //用于将虚拟键消息转换为字符消息
		DispatchMessage(&msg);    //函数分发一个消息给窗口程序
	} 
	return msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM IParam)
{ 
	HDC hdc;    //HDC:设备场景句柄
	PAINTSTRUCT ps;    //PAINTSTRUCT:绘图信息结构
	RECT rect;    //rect:存储成对出现的参数,比如一个矩形框的左上角坐标、宽度和高度
	switch(message)
	{
		case WM_CREATE:
			PlaySound(TEXT("hellowin.wav"), NULL, SND_FILENAME|SND_ASYNC);
			return 0;
		case WM_PAINT:
			hdc = BeginPaint(hwnd, &ps);    //为指定窗口进行绘图工作的准备
			GetClientRect(hwnd, &rect);    //函数获取窗口客户区的大小
			//在指定的矩形里写入格式化的正文
			DrawText(hdc, TEXT("大家好,这是我的第一个Windows API 程序!"), -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
			EndPaint(hwnd, &ps);
			return 0;
		case WM_DESTROY:    //该函数向系统表明有个线程有终止请求
			PostQuitMessage(0);
			return 0;
	}
	return DefWindowProc(hwnd, message, wParam, IParam);    //调用缺省的窗口过程来为应用程序没有处理的任何窗口消息提供缺省的处理
}

标签:窗口,Windows,句柄,编程,程序,wndclass,消息,hwnd
来源: https://www.cnblogs.com/linfangnan/p/15346000.html