欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Windows API入门:6:自己实现MessageBox

程序员文章站 2022-07-05 14:01:33
...

原作者BeYoNDCoDE,主页:http://blog.csdn.net/beyondcode
原文:https://blog.csdn.net/beyondcode/article/details/4034962

SDK编程群号:81543028 欢迎加入

API入门系列文章的第六篇之自己动手实现MessageBox。

我在第四篇文章:一个相当简单的SDK程序 中讲到了通过调用MessageBoxAPI函数来弹出一个对话框,那你有没有想过,我们一句简单的代码背后所隐藏的细节是怎么的呢,那时候让你了解这些未免还早了些,不过现在时机到了,通过一些基本的API函数调用来实现一个自己的对话框其实也不是很难,那么这一篇文章就是这个目的。并顺带介绍一些基本API的应用,由于前面五篇文章的铺垫,我相信你对SDK的程序的大致结构和相关字符处理都有所了解了,所以在这篇文章中的这些知识点,我就不再多说,以免有些人觉得我实在太过啰嗦。实在疑惑的,可以参看前面的文章。

首先我先大概的列出我们为完成这个任务所要用到的一些API函数他

SetWindowText 设置窗口的标题

GetClientRect得到窗口客户区的大小信息

GetWindowLongPtr 通过窗口句柄得到和窗口的相关联信息

CreateWindowEx创建窗口

BeginPaint 得到窗口的设备句柄

EndPaint释放窗口的设备句柄

DrawText 通过设备句柄在窗口上画出文字

好了,就这些函数,我们就能自己实现有一个确定按钮并在确定按钮正上方显示提示信息的简易的对话框了,不过这个对话框可是我们一句一句代码自己实现的哟 还是比较有成就感吧~

程序的大体框架呢还是我们上一篇文章中的框架,注册窗口类,创建一个主窗口,消息循环,窗口消息处理函数。不过要我们需要在窗口消息处理函数中添加一些代码来完成我们需要的功能。在什么地方添加呢? 上个程序,我们只处理WM_DESTROY这个消息。对于这个消息我不再做讲解,不懂的或者忘记了的可以自己MSDN或者看上一篇文章,这里我们要添加对两个消息的处理代码,首先是WM_CREATE,这个消息会在一个窗口被创建的时候被发送到窗口消息处理函数,如果一些事情需要在一个窗口刚被创建的时候执行,那么通过处理WM_CREATE最合适不过啦,代码如下:
首先在winMessageProc开头声明几个变量:

	RECT rctClient,rctText; //用来存放主窗口客户区大小信息
	GetClientRect(hwnd, &rctClient); //得到主窗口客户区的大小信息
	const int buttonWidth = 80; //按钮的宽
	const int buttonHeight = 25; // 按钮的高
	const int textHeight = 25;//文字的高
	HINSTANCE hInst;
	HWND hButton;
	PAINTSTRUCT ps;
	HDC hdc;

下面是switch里的代码:

	case WM_CREATE:
		hInst = (HINSTANCE)GetWindowLongPtr(hwnd, GWLP_HINSTANCE);
		hButton = CreateWindowEx(0L, _T("button"), _T("确定"), 
			WS_VISIBLE | WS_CHILD, rctClient.right / 2 - buttonWidth / 2,
			rctClient.bottom / 2 - buttonHeight / 2, 
			buttonWidth, buttonHeight, hwnd, (HMENU)2, hInst, NULL);
		SetWindowText(hwnd, _T("自定义对话框"));
		break;

在WM_CREATE消息的处理中,我们就用到了GetClientRect,它的第一个参数是窗口的句柄,你想要获取哪个窗口的客户区大小,你就将传递哪个窗口的句柄,第二个参数是一个RECT结构的指针,我上面定义了一个rctClient变量,然后这里把这个变量的地址传递给GetClientRect的第二个参数,让它将所得到的窗口的大小信息保存到这个变量里面。这个函数的具体用法,读者朋友们还可以自己参考MSDN,如果函数调用成功,那么rctClient这个结构体变量中就存放了这个窗口的大小信息了。

然后,我定义了两个整形常量buttonWidth, buttonHeight用来保存我们需要创建的按钮的宽和高。

再然后我调用GetWindowLongPtr这个函数获取和窗口有关的信息,这里获取的是窗口所属的应用程序实例的句柄,也就是WinMain函数所传递进来的第一个参数。在得到这些需要的信息之后,我们就开始着手子窗体的创建了,这里我们要创建的是一个按钮,按钮也是一个窗体,所以也需要窗口类,我们并没有写按钮的窗口类进行注册,那么这个窗口类由谁来注册呢? 其实是有系统创建并注册了按钮的窗口类,窗口类的名字是button,所以我们这里只管用这个窗口类来创建窗口就是了,我们创建主窗口是用的WS_OVERLAPPEDWINDOW这个窗口样式,如果是创建一个子窗口,那么我们需要指定WS_CHILD,如果我们需要创建的窗口能显示出来,那么需要指定WS_VISIBLE这个窗口样式,并且还需要指定创建的窗口所属的父窗口的句柄,如上代码所示。其中第五个参数到第八个参数是该按钮的坐标位置和宽度高度的信息,因为我们需要将该按钮创建在主窗口的*,所以有一系列的计算,具体是怎么计算的,就请各位自己仔细根据上面的代码进行思考了,如果还是有些疑惑,请与我讨论或者加入SDK编程(81543028)群进行讨论交流。

创建完了按钮子窗口,我们还需要将我们的主窗口的标题设置为我们想要的,可以通过SetWindowText这个API函数来完成,第一个参数就是要设置的窗口的句柄,这里为主窗口,所以是我们窗口消息处理函数传递进来的第一个参数hwnd, 第二个参数就是一个字符串指针,指向一个以零结尾的字符串。这里我们就直接将一个字符串常量的首地址传递给它。就完成了主窗口的标题设置。

经过上面这些步骤,我们已经在主窗体的*显示了一个按钮了,并且把主窗口的标题设置为我们自己需要的,但是还要一个问题需要解决,那就是在按钮的正上方显示一串提示文本,怎么来完成呢,这就是我们下面要讲的。

要在主窗口的按钮的正上方显示提示文本信息,就需要得到主窗口的设备句柄,然后通过该设备句柄调用GDI函数DrawText来完成。由于该提示文本需要在每次窗口进行更新的时候绘出,所以我们需要处理WM_PAINT消息来达到这个目的。下面还是先看代码:

	case WM_PAINT:
		hdc = BeginPaint(hwnd, &ps);
		rctText.left = rctClient.left;
		rctText.right = rctClient.right;
		rctText.top = rctClient.bottom / 2 - buttonHeight - textHeight;
		rctText.bottom = rctClient.bottom / 2 - buttonHeight;
		DrawText(hdc, _T("Beyondcode"),
			_tcslen(_T("Beyondcode")), &rctText, 
			DT_CENTER | DT_SINGLELINE | DT_VCENTER);
		EndPaint(hwnd, &ps);
		break;

首先定义了三个整形常量buttonWidthbuttonHeight指示刚才创建的按钮的大小,textHeight指示要显示在文本的矩形框的高度,矩形框的宽度和主窗口的宽度一直,所以就没定义了,然后PAINTSTRUCT 是BeginPaint和EndPaint这两个函数会用到的一个结构体类型,用它定义了一个结构体变量ps,并在调用BeginPaint和EndPaint的时候将它的地址传递给他们的第二个参数。获取一些相关和绘图有关的信息。不过我们这里不会用到,所以就不做详细解释,可以查看MSDN。

注意,BeginPatin这个函数会返回一个设备句柄,然后我们就可以通过这个设备句柄进行绘图,显示文字也是一种绘图,在绘图完毕后,我们需要调用EndPaint这个函数释放刚才得到的哪个设备句柄,也就是是刚才哪个设备句柄无效。而所有的绘图操作,都必须在BeginPaintEndPaint这两个函数之间完成。如上面,通过参数hdc调用DrawText这个函数,因为获取的hdc是通过hwnd这个窗口句柄的,所以这里所有的绘图都会显示在hwnd这个句柄所代表的窗口上,也就是主窗口。rctText是显示文本的矩形的信息,它的大小和位置是通过按钮的大小和当前主窗口的大小信息计算出来的,具体的计算代码中已经写的很清楚了,如有疑惑的可以和我交流交流。 然后还要说的一个就是DT_CENTERDT_VCENTER这两个标志表示在刚才那个矩形框中的水平*和垂直*显示我们的文本,DT_SINGLELINE就是指示单行显示。

最后留给大家一个问题,以供大家思考,上面的程序中,当你改变窗口的大小的时候,就会出现问题,按钮就不会再位于主窗口的*了,怎么解决呢? 我提示一下吧,处理WM_SIZE这个消息。好了,留下这个任务给大家,试试吧~~让按钮随时随地位于主窗口的*。

更详细的注释的完整源代码我会上传群空间供各位下载。

Ok,这章就到这里,由于这些天有些忙,没来得及及时更新,所以多多抱歉了。SDK编程群(81543028)欢迎各位的加入。

完整代码

本文中我们只修改了WinMessageProc函数,所以其他的代码就不贴了。

LRESULT CALLBACK WinMessageProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	RECT rctClient,rctText; //用来存放主窗口客户区大小信息
	GetClientRect(hwnd, &rctClient); //得到主窗口客户区的大小信息
	const int buttonWidth = 80; //按钮的宽
	const int buttonHeight = 25; // 按钮的高
	const int textHeight = 25;//文字的高
	HINSTANCE hInst;
	HWND hButton;
	PAINTSTRUCT ps;
	HDC hdc;
	switch (msg){
	case WM_CREATE:
		hInst = (HINSTANCE)GetWindowLongPtr(hwnd, GWLP_HINSTANCE);
		hButton = CreateWindowEx(0L, _T("button"), _T("确定"), 
			WS_VISIBLE | WS_CHILD, rctClient.right / 2 - buttonWidth / 2,
			rctClient.bottom / 2 - buttonHeight / 2, 
			buttonWidth, buttonHeight, hwnd, (HMENU)2, hInst, NULL);
		SetWindowText(hwnd, _T("自定义对话框"));
		break;
	case WM_PAINT:
		hdc = BeginPaint(hwnd, &ps);
		rctText.left = rctClient.left;
		rctText.right = rctClient.right;
		rctText.top = rctClient.bottom / 2 - buttonHeight - textHeight;
		rctText.bottom = rctClient.bottom / 2 - buttonHeight;
		DrawText(hdc, _T("Beyondcode"),
			_tcslen(_T("Beyondcode")), &rctText, 
			DT_CENTER | DT_SINGLELINE | DT_VCENTER);
		EndPaint(hwnd, &ps);
		break;
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	default:
		return DefWindowProc(hwnd, msg, wParam, lParam);
	}
	return 0;
}