WINDOWS编程基础-最简单的windows程序

时间:2024-02-21 19:23:53

流程

1 建立并注册windows类

2 使用windows类创建窗口

3 实现事件处理,主循环


PeekMessage与GetMessage的对比

相同点:

PeekMessage函数与GetMessage函数都用于查看应用程序消息队列,有消息时将队列中的消息派发出去。

不同点:

无论应用程序消息队列是否有消息,PeekMessage函数都立即返回,程序得以继续执行后面的语句(无消息则执行其它指令,有消息时一般要将消息派发出去,再执行其它指令)。
GetMessage函数只有在消息对立中有消息时返回,队列中无消息就会一直等,直至下一个消息出现时才返回。在等的这段时间,应用程序不能执行任何指令。

游戏时一般使用PeekMessage不阻塞,主循环里除了事件外还要处理绘制逻辑等。

TranslateMessage

将虚拟键消息转换为字符消息。字符消息被送到调用线程的消息队列中,在下一次线程调用函数GetMessage或PeekMessage时被读出将虚拟键
消息转换为字符消息。字符消息被送到调用线程的消息队列中,在下一次线程调用函数GetMessage或PeekMessage时被读出


DispatchMessage

该函数调度一个消息给窗口程序。通常调度从GetMessage取得的消息。消息被调度到的窗口程序即是MainProc()函数



4 实现事件回调处理WinProc



代码

实现上面流程的全部代码,其中全屏相关部分可以去掉(游戏往往才会用到全屏,下面代码主要用来展示win程序创建流程),如果是使用opengl等做游戏,与下面代码不同的地方主要是主循环与绘制方式不同

#include "stdafx.h"
#include "Win_Base.h"

HINSTANCE hInst;		//当前应用的实例句柄
HWND hWnd;				//窗口句柄

bool exiting = false;	//循环开启标志
bool fullscreen=FALSE;	//全屏标志
long windowWidth = 800; //默认窗口宽
long windowHeight = 600;//默认窗口高
long windowBits = 32;   //每像素所选的色彩深度


ATOM RegisterWinClass(HINSTANCE hInstance);
BOOL CreateWin(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int APIENTRY _tWinMain(HINSTANCE hInstance,
	HINSTANCE hPrevInstance,
	LPTSTR    lpCmdLine,
	int       nCmdShow)
{
	MSG msg;
	
	//step1 创建并,注册windows class
	RegisterWinClass(hInstance);

	//step2 创建(全屏或者非全屏)窗口
	if (!CreateWin(hInstance, nCmdShow))
	{
		return FALSE;
	}

	//step3 主循环事件处理
	while (!exiting)
	{
		while (PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE))
		{
			if (!GetMessage (&msg, NULL, 0, 0))
			{
				exiting = true;
				break;
			}

			TranslateMessage (&msg);
			DispatchMessage (&msg);
		}
	}

	if (fullscreen)
	{
		ChangeDisplaySettings(NULL,0);          // If So Switch Back To The Desktop
		ShowCursor(TRUE);                       // Show Mouse Pointer
	}

	return (int) msg.wParam;
}

//创建并,注册windows class
ATOM RegisterWinClass(HINSTANCE hInstance)
{
	WNDCLASSEX wcex;
	wcex.cbSize = sizeof(WNDCLASSEX);
	wcex.style			= CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc	= WndProc;				//时间回调函数指针
	wcex.cbClsExtra		= 0;					//
	wcex.cbWndExtra		= 0;					//
	wcex.hInstance		= hInstance;			//实例
	wcex.hIcon			= LoadIcon(NULL, IDI_APPLICATION);
	wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground	= (HBRUSH)(COLOR_WINDOW+1);
	wcex.lpszMenuName	= NULL;
	wcex.lpszClassName	= "BaseClass";
	wcex.hIconSm		= LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
	return RegisterClassEx(&wcex);
}

//创建(全屏或者非全屏)窗口
BOOL CreateWin(HINSTANCE hInstance, int nCmdShow)
{
	//设置全屏,创建窗口都要用到
	DWORD      dwExStyle;       //扩展窗口风格
	DWORD      dwStyle;         //窗口风格

	RECT       windowRect;		//取得矩形的左上角和右下角的坐标值
	windowRect.left=(long)0;      
	windowRect.right=(long)windowWidth;
	windowRect.top=(long)0;
	windowRect.bottom=(long)windowHeight;

	hInst = hInstance; //使用全局变量存储应用实例(win32工程默认在这个位置赋值)

	//设置全屏状态开始-------------------------------------------------------------
	if (fullscreen)                             
	{
		DEVMODE dmScreenSettings;								//设备模式
		memset(&dmScreenSettings,0,sizeof(dmScreenSettings));	//情况内存
		dmScreenSettings.dmSize = sizeof(dmScreenSettings);		//DEVMODE结构size
		dmScreenSettings.dmPelsWidth = windowWidth;				//屏幕宽
		dmScreenSettings.dmPelsHeight = windowHeight;			//屏幕高
		dmScreenSettings.dmBitsPerPel = windowBits;				//每像素所选的色彩深度
		dmScreenSettings.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT;
		//尝试设置显示模式并返回结果。注: CDS_FULLSCREEN 移去了状态条。
		if (ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL)
		{
			MessageBox(NULL, "Display mode failed", NULL, MB_OK);
			fullscreen = FALSE; 
		}
	}

	//由于全屏模式可能失败,用户可能决定在窗口下运行,我们需要在设置屏幕/窗口之前,再次检查fullscreen的值是TRUE或FALSE。
	//如果我们仍处于全屏模式,设置扩展窗体风格为WS_EX_APPWINDOW,这将强制我们的窗体可见时处于最前面。再将窗体的风格设为WS_POPUP。这个类型的窗体没有边框,使我们的全屏模式得以完美显示。
	//最后我们禁用鼠标指针。当您的程序不是交互式的时候,在全屏模式下禁用鼠标指针通常是个好主意。
	//如果我们使用窗口而不是全屏模式,我们在扩展窗体风格中增加了 WS_EX_WINDOWEDGE,增强窗体的3D感观。
	//窗体风格改用 WS_OVERLAPPEDWINDOW,创建一个带标题栏、可变大小的边框、菜单和最大化/最小化按钮的窗体。
	if (fullscreen)
	{
		dwExStyle=WS_EX_APPWINDOW;
		dwStyle=WS_POPUP;
		ShowCursor(FALSE);
	}
	else
	{
		dwExStyle=WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;   // Window Extended Style
		dwStyle=WS_OVERLAPPEDWINDOW;                    // Windows Style
	}

	AdjustWindowRectEx(&windowRect, dwStyle, FALSE, dwExStyle);//调整窗口达到真正要求的大小

	//设置全屏状态结束-------------------------------------------------------------


	//创建窗口开始---------------------------------------------------------------------
	hWnd = CreateWindowEx(
		NULL,											// 扩展窗体风格
		"BaseClass",									// 类名字
		"Title",										// 窗口标题
		dwStyle |										// 选择的窗体属性							
		WS_CLIPCHILDREN |								// 必须的窗体风格属性
		WS_CLIPSIBLINGS,								// 必须的窗体风格属性
		0, 0,											// 窗口位置
		windowRect.right - windowRect.left,				// 计算调整好的窗口宽度
		windowRect.bottom - windowRect.top,				// 计算调整好的窗口高度
		NULL,											// 无父窗口
		NULL,											// 无菜单
		hInstance,										// 实例
		NULL);											// 不向WM_CREATE传递任何参数

	if (!hWnd)
	{
		MessageBox(NULL,"Window Creation Error.","ERROR",MB_OK|MB_ICONEXCLAMATION);
		return FALSE;
	}
	//创建窗口结束---------------------------------------------------------------------

	ShowWindow(hWnd, nCmdShow);
	UpdateWindow(hWnd);

	return TRUE;
}



LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	int wmId, wmEvent;
	PAINTSTRUCT ps;
	HDC hdc;

	switch (message)
	{
	case WM_PAINT:
		hdc = BeginPaint(hWnd, &ps);
		//绘制代码
		EndPaint(hWnd, &ps);
		break;
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}