您的位置:首页 > 其它

Windows 图形界面笔记(1) - 窗口的显示

2013-12-30 17:49 399 查看
Windows 消息驱动

当Windows 向程序发送消息时,它调用程序中的一个函数,这个函数的参数精确地描述了Windows 发送的消息。在程序中称这个函数为窗口函数(Window Procedure)或消息处理函数。它是一个自定义的回调函数,原形如下。

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

CALLBACK 宏是__stdcall 的意思,
hWnd 参数标识了消息到达的窗口;
uMsg 参数是一个被命名的常量(消息ID 号)
wParam 和lParam 是消息的两个参数,其值取决于uMsg

在桌面上显示一个窗口的具体步骤,这就是主程序的结构流程。

(1)注册窗口类(RegisterClassEx)
(2)创建窗口(CreateWindowEx)
(3)在桌面显示窗口(ShowWindow)
(4)更新窗口客户区(UpdateWindow)
(5)进入无限的消息获取和处理的循环。首先是获取消息(GetMessage),如果有消息到达,则将消息分派到回调函数处理(DispatchMessage),如果消息是WM_QUIT,则 GetMessage 函数返回 FALSE,整个消息循环结束。消息具体的处理过程是在 MainWndProc 函数中进行的。

一个 Windows 基本窗口的实现

// 窗口函数的函数原形
LRESULT CALLBACK MainWndProc(HWND, UINT, WPARAM, LPARAM);

int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
	char szClassName[] = "MainWClass";	
	WNDCLASSEX wndclass;
	
	// 用描述主窗口的参数填充WNDCLASSEX结构
	wndclass.cbSize = sizeof(wndclass);	// 结构的大小
	wndclass.style = CS_HREDRAW|CS_VREDRAW;	// 指定如果大小改变就重画
	wndclass.lpfnWndProc = MainWndProc;	// 窗口函数指针
	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;		// 不指定菜单
	wndclass.lpszClassName = szClassName ;	// 窗口类的名称	
	wndclass.hIconSm = NULL;		// 没有类的小图标
	
	// 注册这个窗口类
	::RegisterClassEx(&wndclass); 
	
	// 创建主窗口
	HWND hwnd = ::CreateWindowEx( 
		0,			// dwExStyle,扩展样式	
		szClassName,		// lpClassName,类名			
		"My first Window!",	// lpWindowName,标题		
		WS_OVERLAPPEDWINDOW,	// dwStyle,窗口风格	
		CW_USEDEFAULT,		// X,初始 X 坐标		
		CW_USEDEFAULT,		// Y,初始 Y 坐标		
		CW_USEDEFAULT,		// nWidth,宽度			
		CW_USEDEFAULT,		// nHeight,高度			
		NULL,			// hWndParent,父窗口句柄			
		NULL,			// hMenu,菜单句柄		
		hInstance,		// hlnstance,程序实例句柄		
		NULL) ;			// lpParam,用户数据			

	if(hwnd == NULL)
	{
		::MessageBox(NULL, "创建窗口出错!", "error", MB_OK);
		return -1;
	}

	// 显示窗口,刷新窗口客户区
	::ShowWindow(hwnd, nCmdShow);
	::UpdateWindow(hwnd);
	
	// 从消息堆中取出消息
	MSG msg;	
	while(::GetMessage(&msg, NULL, 0, 0))
	{
		// 转化键盘消息
		::TranslateMessage(&msg);
		// 将消息发送到相应的窗口函数
		::DispatchMessage(&msg);
	}

	// 当GetMessage返回0时程序结束
	return msg.wParam;
}

LRESULT CALLBACK MainWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	char szText[] = "最简单的窗口程序!";
	switch (message)
	{	 	
	case WM_PAINT: // 窗口客户区需要重画
		{
			HDC hdc;
			PAINTSTRUCT ps;

			// 使无效的客户区变的有效,并取得设备环境句柄
			hdc = ::BeginPaint (hwnd, &ps) ;	
			// 显示文字
			::TextOut(hdc, 10, 10, szText, strlen(szText));
			::EndPaint(hwnd, &ps);
			return 0;
		}	
	case WM_DESTROY: // 正在销毁窗口

		// 向消息队列投递一个WM_QUIT消息,促使GetMessage函数返回0,结束消息循环
		::PostQuitMessage(0) ;
		return 0 ;
	}

	// 将我们不处理的消息交给系统做默认处理
	return ::DefWindowProc(hwnd, message, wParam, lParam);
}

详细说明:

注册窗口类
注册窗口类的API 函数是RegisterClassEx,最后的“Ex”是扩展的意思,因为它是Win16 的RegisterClass 函数的扩展。一个窗口类定义了窗口的一些主要属性,如:图标、光标、背景色和负责处理消息的窗口函数等。这些属性定义在WNDCLASSEX
结构中。
typedef struct _WNDCLASSEX {UINT cbSize; //
WNDCLASSEX 结构的大小

UINT style; // 从这个窗口类派生的窗口具有的风格
WNDPROC lpfnWndProc;// 即 window procedure,
窗口消息处理函数指针
int cbClsExtra; // 指定紧跟在窗口类结构后的附加字节数
int cbWndExtra; // 指定紧跟在窗口事例后的附加字节数
HANDLE hInstance; // 本模块的实例句柄
HICON hIcon; // 窗口左上角图标的句柄
HCURSOR hCursor; // 光标的句柄
HBRUSH hbrBackground;// 背景画刷的句柄
LPCTSTR lpszMenuName;// 菜单名
LPCTSTR lpszClassName;// 该窗口类的名称
HICON hIconSm; // 小图标句柄

} WNDCLASSEX;

wndclass.lpfnWndProc = MainWndProc; // 窗口函数指针

lpfnWndProc 指定了基于此类的窗口的窗口函数,当窗口收到消息时Windows 即自动调用这个函数通知应用程序。

hIcon 和hCursor 为要装载的图标和光标的句柄。

wndclass.hbrBackground = (HBRUSH)::GetStockObject(WHITE_BRUSH); // 使用白色背景画刷

WHITE_BRUSH 是一个Windows 预定义的画刷对象类型,也可以自定义

::RegisterClassEx(&wndclass); // 注册窗口类,失败返回0

创建窗口

用注册的窗口类的类名调用 CreateWindowEx 函数即可,失败返回NULL

第四个参数dwStyle 的值是 WS_OVERLAPPEDWINDOW,即重叠式窗口(Overlapped Window)。其他类型:

WS_BORDER 创建一个单边框的窗口
WS_CAPTION 创建一个有标题框的窗口(包括WS_BODER 风格)

WS_CHIlD 创建一个子窗口。这个风格不能与WS_POPVP 风格合用
WS_DISABLED 创建一个初始状态为禁止的子窗口。一个禁止状态的窗日不能接受来自用户的输人信息
WS_DLGFRAME 创建一个带对话框边框风格的窗口。这种风格的窗口不能带标题条

WS_HSCROLL 创建一个有水平滚动条的窗口
WS_VSCROLL 创建一个有垂直滚动条的窗口
WS_ICONIC 创建一个初始状态为最小化状态的窗口。与WS_MINIMIZE 风格相同
WS_MAXIMIZE 创建一个具有最大化按钮的窗口。该风格不能和WS_EX_CONTEXTHELP 风格同时出现,同时必须指定WS_SYSMENU 风格
WS_OVERLAPPED 产生一个层叠的窗口。一个层叠的窗口有一个标题条和一个边框。与WS_TILED风格相同

 WS_OVERLAPPEDWINDOW 创建一个具有 WS_OVERLAPPED,WS_CAPTION,WS_SYSMENU ,WS_THICKFRAME,WS_MINIMIZEBOX,WS_MAXMIZEBOX风格的层叠窗口
WS_POPUP 创建一个弹出式窗口。该风格不能与WS_CHLD 风格同时使用
WS_POPUPWINDOW 创建一个具有WS_BORDER,WS_POPUP,WS_SYSMENU 风格的窗口,WS_CAPTION 和WS_POPUPWINDOW 必须同时设定才能使窗口某单可见
WS_SIZEBOX 创建一个可调边框的窗口,与WS_THICKFRAME 风格相同
WS_SYSMENU 创建一个在标题条上带有窗口菜单的窗口,必须同时设定 WS_CAPTION 风格
WS_THICKFRAME 创建一个具有可调边框的窗口,与WS_SIZEBOX 风格相同
WS_VISIBLE 创建一个初始状态为可见的窗口

在桌面显示窗口

::ShowWindow(hwnd, nCmdShow);

更新窗口客户区

::UpdateWindow(hwnd);

如果指定窗口的更新区域不为空的话,UpdateWindow 函数通过向这个窗口发送一个 WM_PAINT 消息更新它的客户区。当窗口显示在屏幕上时,窗口的客户区被在 WNDCLASSEX 中指定的刷子擦去了,调用 UpdateWindow 函数将促使客户区重画,以显示其内容。

进入无限的消息循环

在调用UpdateWindow 函数之后,整个窗口已经显示在桌面上,程序必须准备从用户接收键盘和鼠标输入了。Windows 为每个线程维护了一个消息队列,每当有一个输入发生,Windows 就把用户的输入翻译成消息放在消息队列中。利用 GetMessage 函数可以从调用线程的消息队列中取出一个消息来填充MSG 结构。如果消息队列中没有消息(即没有用户输入),这个函数会一直等待下去,直到有消息进入到消息队列为止。

::GetMessage(&msg, NULL, 0, 0);

GetMessage 函数从消息队列中取得的消息如果不是WM_QUIT,则返回非零值。一个 WM_QUIT 消息会促使GetMessage 函数返回 0,从而结束消息循环。

MSG 结构类型

typedef struct tagMSG {

HWND hwnd; // 消息要发向的窗口句柄
UINT message; // 消息标识符,以WM_ 开头的预定义值(意为Window Message)
WPARAM wParam;
// 消息的参数之一
LPARAM lParam; // 消息的参数之二
DWORD time; // 消息放入消息队列的时间
POINT pt; // 这是一个POINT 数据结构,表示消息放入消息队列时的鼠标位置

} MSG, *PMSG ;

DispatchMessage 函数分发一个消息到对应窗口的窗口函数。在上面的例子中,窗口函数是MainWndProc。MainWndProc 处理消息后把控制权交给Windows,此时 DispatchMessage 函数仍然在继续工作,当它返回时,消息循环从调用GetMessage 函数开始进入下一轮循环。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐