一个基于Windows API的简单窗口程序

了解Windows程序内部运行机制与做一个基于Windows API的简单窗口程序

目录

  • ​ 目的
  • ​ 开发环境
  • ​ 初识Windows
  • ​ 总结

目的

1、了解Windows操作系统应用程序基础原理概念;

2、掌握WinMain函数定义,创建窗口,消息循环及窗口过程函数;与动手做一个简单的Windows窗口程序

开发环境

1、Visual Studio 2017

初识Windows

学习Windows窗口应用程序的第一步是了解WinMain函数

同DOS程序相同,Windows同样有它自己的Main函数——>WinMain函数,也就是Windows的入口函数,它使得Windows启动与结束。
一个Windows程序的运行步骤:

1、WinMain函数的定义

2、创建一个窗口

3、进行消息循环

4、编写窗口过程函数

一个简单的例子来说明WinMain函数的定义

一个基于Windows API的简单窗口程序_第1张图片

最简单的Windows程序就是如此显示一个消息窗口,代码如下:

#include
int WINAPI WinMain(
	HINSTANCE hInstance, 
	HINSTANCE hPrevInstance, 
	LPSTR lpCmdLine, 
	int nShowCmd)
{
     

	MessageBox(NULL, "Visual C++", "窗口消息", 0); //显示消息对话框,MessageBox(句柄,显示内容,标题,MB_OK[此参数类型多种,如:MB_YESNO,MB_OKCANCEL等])
	return 0;
}

创建一个窗口(均用实例中的代码进行说明)

创建一个Windows窗口需要经过以下操作步骤:

1、设计一个窗口类;

2、注册窗口类;

3、创建窗口;

4、显示及更新窗口。

1️⃣、设计一个窗口类

	WNDCLASS wn1;											//定义一个窗口类
	wn1.style = CS_HREDRAW | CS_VREDRAW;					//窗口类的类型
	wn1.lpfnWndProc = Win1;									//窗口的过程函数(回调函数)
	wn1.cbClsExtra = 0;										//窗口类的附加内存,一般设置为0
	wn1.cbWndExtra = 0;										//窗口附加内存空间,通常设置为0
	wn1.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);//画刷句柄通俗的说就是窗口的背景
	wn1.hCursor = LoadCursor(NULL, IDC_APPSTARTING);		//光标句柄;使用系统预定义光标时,第一个参数为NULL,第二参数有很多,仅限于系统预订光标
	wn1.hIcon = LoadIcon(NULL, IDI_ERROR);					//图标句柄
	wn1.hInstance = hInstance;								//定义一个程序实例句柄,后面会用到
	wn1.lpszClassName = "FIRST";							//设置类的名称
	wn1.lpszMenuName = NULL;								//设置菜单的名字

Windows对窗口类定义原型如下:

typedef struct WNDCLASS{
     
	UINT style;//窗口类的类型
	WNDPROC lpfnWndProc;//窗口的过程函数(回调函数)
	int cbClsExtra;//窗口类的附加内存,一般设置为0
	int cbWndEXtra;//窗口附加内存空间,通常设置为0
	HISNSTANCE hInstance;//实例号
	HICON hIcon;//图标句柄
	HCURSOR hCursor;//光标句柄
	HBRUSH hbrBackground;//画刷句柄
	LPCTSTR lpszMenuName;//设置菜单的名字
	LPCTSTR lpszClassName;//设置类的名称
}WNDCLASS;

2️⃣、注册窗口类

RegisterClass(&wn1);									//注册窗口

Windows对注册窗口类函数原型如下:

WINUSERAPI
 2   ATOM
 3   WINAPI
 4   RegisterClassA(
 5       __in CONST WNDCLASSA *lpWndClass);
 6   WINUSERAPI

3️⃣、创建窗口

HWND hwnd;
	hwnd = CreateWindow("FIRST", "一个窗口", WS_OVERLAPPEDWINDOW, 500, 500, 600, 400, NULL, NULL, hInstance, NULL
					//(窗口名称,窗口标题,窗口风格,初始X坐标,初始Y坐标,X方向尺寸,Y方向尺寸,父窗口句柄,窗口菜单句柄,程序实例句柄,创建参数)
	);

CreateWindows函数的原型声明如下:

HWND CreateWindow(
	LPCTSTR lpClassName,		//窗口类名称
	LPCTSTR lpWindowName,		//窗口的名称
	DWORD dwstyle,				//创建窗口的样式
	int x,				  	 //窗口左上角x坐标
	int y,					//窗口左上角y坐标
	int nWidth,				//窗口宽度
	int nHeight,			//窗口高度
	HWND hWndParent,		//创建窗口的父窗口句柄
	HMENU hMenu,			//窗口菜单的句柄
	HANDLE hInstance,		//窗口所属的应用程序的实例句柄
	LPVOID lpParam			//数据指针
);

4️⃣、显示窗口及更新

ShowWindow(hwnd, SW_SHOWNORMAL);	//显示窗口函数
UpdateWindow(hwnd);				//更新窗口函数

显示及更新窗口函数ShowWindow原型声明如下:

//显示窗口
BOOL ShowWindow(
	HWND hWnd,//窗口句柄
	int nCmdShow//窗口显示状态
);
//更新窗口
BOOL UpdateWindow(
	HWND hWnd//创建成功后的窗口句柄
)

到此为止,一个简单的窗口就创建完毕了。

消息循环

MSG结构体定义如下

typedef struct tagMSG{
     
	HWND hWnd;//窗口表示,代表消息所属窗口
	UINT message;//消息标识
	WPARAM wParam;//消息附加信息
	LPARAM lParam;//消息附加信息
	DWORD time;//消息投递到消息队列的时间
	POINT pt;//鼠标当前位置
}

通常编写消息循环用如下方式

MSG msg;
	while (GetMessage(&msg, NULL, 0, 0))
	{
     
		TranslateMessage(&msg);  //将虚拟键消息转换为字符消息
		DispatchMessage(&msg);	//将消息回传给操作系统
	}

Windows的GetMessage()函数如下

BOOL GetMessage(
	LPMSG lpMsg,//指向消息结构体
	HWND hWnd,//接收属于哪个窗口的消息
	UINT wMsgFilteMin,//获取消息的最小值
	UINT wMsgFilteMax//获取消息的最大值
);

消息将进入消息队列中,当下一次调用GetMessage函数时被取出。取出后的消息被TranslateMessage函数转换为新消息投递到线程的消息队列中。而后DispatchMessage函数对消息进行处理(响应)

下面是Windows应用程序的消息处理机制图以便更好的理解(采自孙鑫先生的《VC++深入详解》)

一个基于Windows API的简单窗口程序_第2张图片

编写窗口过程函数

LRESULT CALLBACK Win1(
	HWND hwnd,//窗口句柄
	UINT uMsg,//消息代码
	WPARAM wParam,//消息附加参数
	LPARAM lParam//消息附加参数
)
{
     
	switch (uMsg)
	{
     
	case WM_CHAR:
		char show[20];
		scanf_s(show, "char is %d", wParam);
		MessageBox(hwnd, show, "char", 0);
		break;
	case WM_LBUTTONDOWN:
		MessageBox(hwnd, "Windows!", "消息窗口", MB_OKCANCEL);
		HDC hdc;
		hdc = GetDC(hwnd);
		TextOut(hdc, 50, 100, "我做的", strlen("我做的"));
		break;
	case WM_PAINT:
		HDC hDC;
		PAINTSTRUCT ps;
		hDC = BeginPaint(hwnd, &ps);
		TextOut(hDC, 100, 100, "第一个Windows!", strlen("第一个Windows!"));
		EndPaint(hwnd, &ps);
		break;
	case WM_CLOSE:
		if (IDYES == MessageBox(hwnd, "是否真的结束?", "message", MB_YESNO))
		{
     
			DestroyWindow(hwnd);
		}
		break;
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	default:
		return DefWindowProc(hwnd, uMsg, wParam, lParam);
	}
	return 0;
}

以下看看我们做的第一个窗口是什么样子的

一个基于Windows API的简单窗口程序_第3张图片

点击鼠标后

一个基于Windows API的简单窗口程序_第4张图片

点击确定按钮

一个基于Windows API的简单窗口程序_第5张图片

按下键盘键

一个基于Windows API的简单窗口程序_第6张图片

选择关闭窗口

一个基于Windows API的简单窗口程序_第7张图片

总结

由于我用的是VS2017的WIN32环境,与书中所讲的VC++6.0编译环境有所不同,在最初接触VS时遇到了些许困难,但都一一解决,在此总结下来,并分享给大家

①在VC++6.0中使用spritf是可行的在VS中需要加入_s,即sprintf_s,否则就会出现烫烫烫…的乱码如图(其实在VS2017中,哪怕是用sprintf也是会报错的)
一个基于Windows API的简单窗口程序_第8张图片
②在配置VS2017环境时,需要将字符集改为多字节字符集。如图一个基于Windows API的简单窗口程序_第9张图片
否则则会与LRESULT的类型不兼容。
③在用VS2017建立Windows桌面应用程序时一定要点击正确,在项目中的子系统与预编译器中要有以下的东西,预处理器定义中有WINDOWS;子系统是窗口而非控制台,否则会一直出现“无法解释外部符号_WinMain@16…”(当然每个电脑可能配置不同,需要用到的同学可以看看)附图
一个基于Windows API的简单窗口程序_第10张图片
一个基于Windows API的简单窗口程序_第11张图片
问题基本上就这些。但是这样做出来的窗口是比较丑陋的,特别是右上角的那个红色的×。

再进一步

在这里插入图片描述
在网络上制作一个ico文件,添加到resource文件夹中,在上面提到的光标句柄中进行修改

wn1.hCursor = LoadCursor(NULL, IDC_APPSTARTING);

如此就一点点的对窗口进行内容丰富和美化

你可能感兴趣的:(windows)