昔洛 的个人博客

Bug不空,誓不成佛

  menu
70 文章
14633 浏览
4 当前访客
ღゝ◡╹)ノ❤️

Windows窗口创建及消息机制

一、windows程序部分概念的解释

①句柄(HANDLE),资源的标识

操作系统要管理和操作这些资源,都是通过句柄来找到对应的资源。按资源的类型,又可将句柄细分成图标句柄(HICON)光标句柄(HCURSOR)窗口句柄(HWND)应用程序实例句柄(HINSTANCE)等。操作系统给每一个窗口置顶的一个唯一的标识号即窗口句柄。

② 关于消息及消息队列

操作系统将每个消息事件都包装成一个成为消息的结构体(MSG)通过消息机制Message来传递给应用程序。
  结构体MSG定义如下:

typedef struct tagMSG{
    HWND hwnd;        //指向窗口的句柄,指定哪个窗口接收这个消息
    UINT message;     //一个无符号整形类型的消息类型,比如按键,点击
    WPARAM wParam;    //消息的附加信息,比如按下的键是某个字母以ASCII表示
    LPARAM lParam;    //消息的附加信息,其他信息
    DWORD time;       //DWORD 双16及32位整形,指示消息投递的时间
    POINT pt;         //一个点的结构体POINT,指示当消息被投递的时候光标的位置
}MSG,*PMSG;

③ WinMain函数

WinMain函数是windows程序的入口函数其定义如下:

int WINAPI WinMain(  
	HINSTANCE hInstance,	//应用程序的实例句柄,及本程序运行的句柄由操作系统传入
	HINSTANCE hPrevInstance,//与其相关的兄弟实例,加入已经存在一个相同的程序,本程序为第二个,那么该参数则为先前程序的实例句柄
	LPWSTR lpCmdLine,	//命令行参数,类型为字符串指针类型
	int nShowCmd		//窗口显示的状态参数
); 

④ 过程函数(回调函数)

当应用程序收到给某一窗口的消息时,就应该调用某一函数来处理这条消息。这一调用过程不用应用程序自己来实施,而由操作系统来完成。对于一条消息,操作系统调用的就是接收消息的窗口所属的类型中的lpfnWndProc成员指定的函数。每一种不同类型的窗口都有自己专用的回调函数。

二、窗口的创建

创建一个完整的窗口需要经过下面四个操作步骤:

①设计一个窗口类

窗口类WNDCLASS定义如下

typedef struct _WNDCLASS {
  UINT style; 		//窗口类的特征类型,可以用或运算赋予多个属性值,其变量对应的位为1 例如CS_HREDRAW | CS_VREDRAW当改变窗口宽度和高度时重画窗口
  WNDPROC lpfnWndProc; 	//指定该窗口的一个回调函数
  int cbClsExtra; 	//窗口类的额外空间数据,通常为0
  int cbWndExtra; 	//为窗口附加内存空间,通常为0单位同上都为字节
  HANDLE hInstance; 	//当前应用程序实例句柄,可为WinMain传入的实例句柄
  HICON hIcon; 		//图标句柄,使用HICON LoadIcon(HINSTANCE hInstance,  LPCTSTR lpIconName); 来加载详细可查MSDN
  HCURSOR hCursor; 	//光标句柄,用法与HICON非常相类似可MSDN查找LoadCursor()的用法
  HBRUSH hbrBackground; //背景画刷句柄可使用GetStockObject(int fnObject)来返回一个画刷需强制转换一下
  LPCTSTR lpszMenuName; //指定菜单的名字没有则为NULL
  LPCTSTR lpszClassName;//类的名字,也就是本注册窗口的一个名字
} WNDCLASS;

②注册窗口类

创建了一个窗口类后要为这个窗口类进行注册使用如下定义:

ATOM RegisterClass(
  const WNDCLASS* lpWndClass	//窗口类的地址
);
创建成功则返回一个唯一的标识id,0则表示创建失败。

③创建窗口

前期工作,创建窗口类,注册窗口类,接下来就是创建窗口了,创建窗口的api定义如下:

HWND CreateWindow(
	LPCTSTR lpClassName,	//窗口类的名字,要与定义的窗口类的名字保持一致,否则将无法创建窗口
	LPCTSTR lpWindowName, 	//窗口标题的名字
	DWORD dwStyle,		//窗口的类型,与窗口类的类型不同
	int x,			//窗口的水平x坐标
	int y,			//窗口的垂直y坐标
	int nWidth,		//窗口的宽度
	int nHeight, 		//窗口的高度
	HWND hWndParent, 	//该窗口的父窗口实例句柄,没有则为NULL
	HMENU hMenu,		//菜单的实例句柄,没有则为NULL
	HANDLE hInstance,	//本应用程序的实例句柄
	PVOID lpParam		//指向传递给窗口的附加消息数据指针
);

④显示及更新窗口

创建完毕后使用ShowWindow()将窗口展示出来

BOOL ShowWindow(
   HWND hWnd,	//要显示的窗口
   int nCmdShow	//显示窗口的类型
); 

BOOL UpdateWindow(
  HWND hWnd   //刷新窗口显示
);

三、消息循环

while(GetMessage(&msg,NULL,0,0))
{
	TranslateMessage(&msg);
	DispatchMessage(&msg);
}

GetMessage定义如下:

BOOL GetMessage(
  LPMSG lpMsg,		//MSG结构体的指针
  HWND hWnd,		//要获取那个窗口的消息,如果为NULL则获取任何窗口的消息
  UINT wMsgFilterMin, 	//获取最小消息值同下设置为0则获取所有消息
  UINT wMsgFilterMax	//获取最大消息值
); 

转换,翻译消息

BOOL TranslateMessage(//将WM_KEYDOWN等类型消息转换为WM_CHAR等类型消息
  const MSG* lpMsg
); 

传递消息给回调函数

LONG DispatchMessage(//将消息传递给窗口的回调函数进行处理
  const MSG* lpmsg
);

四、 窗口回调函数介绍

LRESULT CALLBACK WindowProc(//函数名字任意
  HWND hwnd,	//窗口句柄
  UINT uMsg,	//消息类型
  WPARAM wParam,//第一个消息参数
  LPARAM lParam	//第二个消息参数
); 

处理消息

switch(uMsg)
{
	case WM_PAINT:break;		//当窗口重绘时,简单说下有两组绘画句柄,BeginPaint()/EndPaint() 和 GetDC()/ReleaseDC() 两个不能相互混用
	case WM_CHAR:break;		//此时wParam则为按键的ascii值
	case WM_LBUTTONDOWN:break;	//鼠标左键按下
	case WM_CLOSE:break;		//关闭请求可用DestroyWindow销毁窗口
	case WM_DESTROY:break;		//当窗口销毁时(进程不会销毁),此时用PostQuitMessage(0);来处理这样就可以关闭整个程序了。
}

五、 简单的一个窗口程序

#include <windows.h>

#include <stdio.h>

LRESULT CALLBACK WinSunProc(  HWND hwnd,   UINT uMsg,   WPARAM wParam,   LPARAM lParam )
{
	switch(uMsg)
	{
	case WM_CHAR:
		{
			char szChar[20];
			sprintf(szChar,"char is %d",wParam);
			MessageBox(hwnd,szChar,"demo",0);
			break;
		}
	case WM_LBUTTONDOWN:
		{
			MessageBox(hwnd,"鼠标左键点击","demo",0);
			HDC hdc = GetDC(hwnd);
			TextOut(hdc,0,50,"计算机编程语言练习",strlen("计算机编程语言练习"));
			ReleaseDC(hwnd,hdc);
			break;
		}
	case WM_PAINT:
		{
			HDC hdc;
			PAINTSTRUCT paint;
			hdc = BeginPaint(hwnd,&paint);
			TextOut(hdc,0,0,"项目练习",strlen("项目练习"));
			EndPaint(hwnd,&paint);
			break;
		}
	case WM_CLOSE:
		{
			if(IDYES == MessageBox(hwnd,"是否确定退出","demo",MB_YESNO))
			{
				DestroyWindow(hwnd);
			}
			break;
		}
	case WM_DESTROY:
		{
			PostQuitMessage(0);
			break;
		}
	default:return DefWindowProc(hwnd,uMsg,wParam,lParam);
	}

	return 0;
}

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd )
{
	::WNDCLASS wndcls;
	wndcls.cbClsExtra = 0;
	wndcls.cbWndExtra = 0;
	wndcls.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
	wndcls.hIcon = LoadIcon(NULL,IDI_ERROR);
	wndcls.hCursor = LoadCursor(NULL,IDC_ARROW);
	wndcls.hInstance = hInstance;
	wndcls.lpfnWndProc = WinSunProc;
	wndcls.lpszClassName = "Demo";
	wndcls.lpszMenuName = NULL;
	wndcls.style = CS_HREDRAW | CS_VREDRAW;
	::RegisterClass(&wndcls);
	HWND hwnd;
	hwnd = CreateWindow("Demo","简单的窗口练习",WS_OVERLAPPEDWINDOW,0,0,600,400,NULL,NULL,hInstance,NULL);
	::ShowWindow(hwnd,SW_SHOW);
	::UpdateWindow(hwnd);
	MSG msg;
	while(::GetMessage(&msg,NULL,0,0))
	{
		::TranslateMessage(&msg);
		::DispatchMessage(&msg);
	}
	return 0;
}
内事不懂问百度,外事不懂问谷歌~