Win32窗体入门

  和MS-DOS或控制台编程不同, Win32子系统的绝大多数应用程序是以窗体为基础建立的, 所以学习Win32编程, 就必须要懂得如何建立窗体, 本例简要的介绍了如何去建立一个Windows窗体以及其最基本的工作原理。

  主要包括:WinMain主函数;窗体类;创建窗体;消息循环;窗体消息处理

程序清单:

1. Main.c

#include <stdio.h>
#include "WinClasses.h"

/**
 *    WinMain函数和wWinMain函数是Windows图形子系统进程
 * 启动的主函数, 作用和main函数相同, 只不过Windows不会为具
 * 有WinMain函数(或wWinMain函数)的进程创建控制台窗口。
 *    如果操作系统支持UNICODE字符集, 则系统会呼叫程序中的
 * wWinMain函数, 否则呼叫程序中的WinMain函数。
 *
 *    _tWinMain宏的作用是:在定义了_UNICODE宏或UNICODE
 * 宏时, 表示wWinMain,否则表示WinMain。
 *
 * 参数:hInstance, 表示当前进程的句柄
 *         hPrevInstance, 表示前一个进程的句柄(不用)
 *         lpCmdLine, 由操作系统传入的命令行参数
 *         nCmdShow, 一个整数值, 操作系统传入如何创建窗口的
 * 标志
 */
int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
	// 用于显示错误信息的字符串缓冲
	TCHAR szError[BUFSIZ] = _T("");

	// 所建立窗口的句柄
	HWND hWnd = NULL;

	// 消息结构体, 用于消息循环
	MSG msg;
	ZeroMemory(&msg, sizeof(msg));

	// 创建窗口:

	// 步骤1:注册窗口类, 每一个被创建的窗口都必须属于一个窗口类, 这个概念和面向对象编程很类似
	if (RegistMainWinClass(hInstance) == 0) 
	{   
		_stprintf_s(szError, BUFSIZ, _T("出现错误, 无法注册窗口, 错误代码:%d。"), GetLastError());

		// 显示一个消息框
		MessageBox(
			NULL,        // 消息框所属的窗体, NULL表示消息框不属于任何窗体(此时还没有窗体被建立)
			szError,    // 显示在消息框内部的文字
			_T("错误"),    // 显示在消息狂标题栏的文字
			MB_OK | MB_ICONERROR    // 消息框样式, MB_OK表示具备一个确定按钮, MB_ICONERROR表示显示一个错误图标
		);
	} 
	else 
	{
		// 步骤2:创建窗口
		hWnd = CreateWindowEx(
			0,				// 创建窗口的扩展样式, 0为默认样式
			MAIN_CLASSNAME,	// 要创建窗口所属的窗口类名称
			_T("Hello World"),	// 窗口标题字符串
			WS_OVERLAPPEDWINDOW,	// 窗口样式, WS_OVERLAPPEDWINDOW为一个普通窗体具有标题栏, 系统菜单, 最大化按钮和边框架
			CW_USEDEFAULT,	// 设置窗体左上角相对于屏幕显示的x坐标, CW_USEDEFAULT表示显示在默认位置
			0,	// 设置窗体左上角相对于屏幕显示的y坐标,当x坐标设置为CW_USEDEFAULT, 该参数被忽略
			CW_USEDEFAULT,	// 设置窗体的宽度, CW_USEDEFAULT表示窗体为默认宽度(约为整个屏幕的2/3)
			0,		// 设置窗体的高度, 当宽度设置为CW_USEDEFAULT时, 该参数被忽略
			NULL,	// 设置该窗体的父窗体, 目前没有父窗体
			NULL,	// 设置该窗体的菜单, 目前没有菜单
			hInstance,	// 设置该窗体所属的进程句柄
			NULL	// 设置该窗体的显示参数, 该参数将通过WM_CREATE消息发送到消息队列
		);
		// CreateWindowEx返回一个表示窗口的句柄, 如果返回值为NULL, 表示创建窗口失败
		if (hWnd == NULL) 
		{
			_stprintf_s(szError, BUFSIZ, _T("出现错误, 无法创建窗口, 错误代码:%d。"), GetLastError());
			MessageBox(NULL, szError, _T("错误"), MB_OK | MB_ICONERROR);
		} 
		else 
		{
			// 步骤3:显示并刷新窗口
			// 如果窗口创建成功, 则进一步需要显示该窗口(ShowWindow), 紧接着刷新一次窗口(UpdateWindow)
			ShowWindow(
				hWnd,	// 要显示的窗口句柄
				nCmdShow	// 显示窗口的方式, 这里采用系统传入的nCmdShow, 即由操作系统来决定如何显示该窗口
			);
			UpdateWindow(hWnd);

			// 步骤4:启动消息循环
			// 消息循环是Windows窗体程序得以运转的核心, 这里先做简要介绍
			while (GetMessage(
				&msg,	// 一个 MSG 结构体变量的指针, 调用该函数可以获取到最新发来的消息
				NULL,	// 一个窗体句柄, 表示接收发送给某个窗体的消息, 如果为 NULL, 表示接收所有消息
				0,		// 后两个参数表示消息过滤范围, 此处不适用
				0
			)) 
			{
				// 转化键盘消息
				TranslateMessage(&msg);
				// 将消息送入消息处理函数, 即在注册窗体类时填入的函数指针指向的函数
				DispatchMessage(&msg);
			}
		}
	}

	// 一般来说, 主函数返回消息结构体的 wParam 域
	return msg.wParam;
}

2.1 WinClasses.h

#pragma once

#include "WinProcs.h"

// 注册窗体类使用的类名
#define MAIN_CLASSNAME _T("Hello-Win")

// 注册窗体函数
ATOM WINAPI RegistMainWinClass(HINSTANCE hIns);

2.2 WinClasses.c

#include "WinClasses.h"

/**
 * 注册主窗体类。
 * 参数:hIns 进程句柄
 * 返回:如果窗体注册成功, 返回窗体类注册编号(一个唯一值);返回0表示窗体注册失败
 */
ATOM WINAPI RegistMainWinClass(HINSTANCE hIns)
{
	// 窗体类是一个结构体, 结构体的每一个分量域都有特殊的含义, 需仔细填写
	WNDCLASSEX wcex;
	ZeroMemory(&wcex, sizeof(wcex)); // 首先进行初始化, 将结构体占据内存清空

	// cbSize, 填入WNDCLASSEX结构体的大小, 在SDK早期版本里, 使用WNDCLASS结构体
	// 目前使用 WNDCLASSEX 结构体, cbSize 域就是用于区别这两个结构体的
	wcex.cbSize = sizeof(wcex);       

	// 设置窗体绘制方式, 横向绘制和纵向绘制
	wcex.style = CS_HREDRAW | CS_VREDRAW;

	// 设置处理该窗口消息的回调函数指针
	wcex.lpfnWndProc = WndMainProc;

	// 设置该窗体类所属的进程句柄
	wcex.hInstance = hIns;

	// 设置该窗体类普通图标
	wcex.hIcon = LoadIcon(hIns, MAKEINTRESOURCE(IDI_APPLICATION));

	// 设置该窗体类鼠标指针类型
	wcex.hCursor = LoadCursor(NULL, IDC_ARROW);

	// 设置该窗体类背景颜色
	wcex.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);

	// 设置该窗体类类名
	wcex.lpszClassName    = MAIN_CLASSNAME;

	// 设置该窗体类小图标
	wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_APPLICATION));

	return RegisterClassEx(&wcex);
}

3.1 WinProcs.h

#pragma once

#include <tchar.h>
#include <windows.h>

// 用于处理主窗体消息的消息处理函数
LRESULT CALLBACK WndMainProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);

3.2 WinProcs.c

#include "WinProcs.h"
#include "Draw.h"

/**
 * 处理窗口消息的回调函数
 * 参数:hWnd, 窗口句柄, 表示消息的来源窗体
 *	     message, 消息标识
 *	     wParam, 消息参数1
 *	     lParam, 消息参数2
 * 返回:整数值, 表示该消息处理的结果
 */
LRESULT CALLBACK WndMainProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	// 绘图上下文句柄
	HDC hDC = NULL;

	// 绘图结构体
	PAINTSTRUCT ps;

	LRESULT lReturn = 0L;
	switch (message) 
	{
	case WM_CREATE:    // 窗口被创建的消息
		break;
	case WM_PAINT:    // 窗口重绘消息
		hDC = BeginPaint(hWnd, &ps);
		// 调用绘制字符串函数显示内容
		WndMainDrawString(hWnd, hDC);
		EndPaint(hWnd, &ps);
		break;
	case WM_CLOSE: // 窗口被关闭的消息
		if (MessageBox(hWnd, _T("是否要退出应用程序?"), _T("提示"), MB_YESNO | MB_ICONQUESTION) == IDYES) 
			DestroyWindow(hWnd);
		break;
	case WM_DESTROY: // 窗口被撤销的消息, 
		// 一般来说, 只有主窗体被关闭, 才能获得这个消息
		PostQuitMessage(0);
		break;
	default:
		// 对于其它未处理的消息, 调用默认消息处理函数处理, 这是必须的
		lReturn = DefWindowProc(hWnd, message, wParam, lParam);
	}
	return lReturn;
}

3.1 Draw.h

#pragma once
#include <tchar.h>
#include <windows.h>

// 要显示的字符串
#define SHOW_STRING _T("Hello World,这是第一个 Win32 窗体程序")

// 显示字符串的函数
void WINAPI WndMainDrawString(HWND hWnd, HDC hDC);

3.2 Draw.c

#include "Draw.h"

// 保存字体句柄的变量
static HFONT hFont = NULL;

/**
 * 绘制字符串的函数
 * 参数:hWnd, 要绘制字符串的窗体句柄
 *         hDC,用于绘制的上下文句柄
 */
void WINAPI WndMainDrawString(HWND hWnd, HDC hDC)
{
	// 用于获取窗体工作区矩形尺寸的结构体
	// 由四个域组成 long left, long top, long width, long height 
	RECT rect;

	// 保存上下文当前字体句柄的变量,用于恢复原先字体
	HGDIOBJ hOldFont;

	// 保存上下文当前字体颜色的变量,用于恢复原先字体颜色
	COLORREF cOldColor;

	// 如果字体未被创建,则创建字体
	if (hFont == NULL) 
	{
		// 字体信息结构体
		LOGFONT logFont;
		ZeroMemory(&logFont, sizeof(logFont));

		// 设置字体的字符集
		logFont.lfCharSet = GB2312_CHARSET;

		// 设置字体的高度和宽度
		logFont.lfHeight = 30;
		logFont.lfWidth = logFont.lfHeight * 0.75;

		// 设置字体名称
		_tcscpy_s(logFont.lfFaceName, 32, _T("隶书"));

		// 创建字体
		hFont = CreateFontIndirect(&logFont);
	}
   
	// 将创建的字体选入上下文中,返回之前的字体句柄
	hOldFont = SelectObject(hDC, hFont);

	// 获取窗体用户区尺寸
	GetClientRect(hWnd, &rect);

	// 设置字体颜色,返回之前的颜色变量
	cOldColor = SetTextColor(hDC, COLOR_WINDOWTEXT);

	// 绘制字体
	DrawText(
		hDC,		// 要绘制字体的上下文
		SHOW_STRING,		// 要绘制字体的字符串指针
		-1,		// 字符串长度,-1表示自行测量
		&rect,	// 要绘制字体所在窗口的矩形范围
		DT_SINGLELINE | DT_CENTER | DT_VCENTER	// 绘制方式 单行 | 水平居中 | 垂直居中
	);

	// 恢复绘图前上下文的状态
	SelectObject(hDC, hOldFont);
	SetTextColor(hDC, cOldColor);
}


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值