win32基础界面开发

项目创建

在VS2022中选择空项目,点击下一步,输入相关信息后点击创建

用鼠标右键点击右边解决方案下的项目名字,打开属性页,将配置改为所有配置平台改为所有平台

接着找到配置属性中的链接器中的系统,将子系统控制台 (/SUBSYSTEM:CONSOLE)改成窗口 (/SUBSYSTEM:WINDOWS),点击确定。

新建一个main.c源文件:

#ifndef UNICODE
#define UNICODE
#endif 

启用UNICODE调用unicode版本的函数

引入头文件:

#include <windows.h>

win32 gui 应用的入口函数:

int WINAPI  WinMain(
	_In_ HINSTANCE hInstance,
	_In_opt_ HINSTANCE _,
	_In_ LPSTR     lpCmdLine,
	_In_ int       nShowCmd
) {
	return 0;
}

其中的_In_或者_In_opt_或者_Out_都是微软自己拓展的东西,给预处理器和人看的,经过预处理器处理后不会留下了,_In_表示是输入参数,_Out_是输出参数,_In_opt_ 表示是可选的输入参数。

参考链接: winMain函数(winbase.h)

注册 Window 类

想要显示一个window,首先需要注册Window类。

注册窗口类的第一步是使用窗口类信息填充WNDCLASSEXW结构。并该将结构传递给 RegisterClassExW函数。

const wchar_t MainClassName[] = L"Goodbye";
WNDCLASSEXW mainWndClass;
// 不初始化的话,可能会导致RegisterClassExW调用失败
memset(&mainWndClass, 0, sizeof(WNDCLASSEXW));
mainWndClass.hInstance = instance;
// 窗口消息处理函数
mainWndClass.lpfnWndProc = MainWndProc;
mainWndClass.cbSize = sizeof(WNDCLASSEXW);
mainWndClass.lpszClassName = MainClassName;
mainWndClass.style = CS_HREDRAW|CS_VREDRAW;
// 没有这一行代码的话,改变窗口大小会变成黑色
mainWndClass.hbrBackground = (HBRUSH)COLOR_WINDOWFRAME;

if (0 == RegisterClassExW(&mainWndClass)) {
	ErrorExit(L"RegisterClassExW");
}

每一个window都有自己的lpszClassName(类名),每个window的类名都不一样。

cbSize设置为 sizeof(WNDCLASSEXW)。 在调用 GetClassInfoExW 函数之前,请务必设置此成员。

style设置为CS_HREDRAW|CS_VREDRAW表示窗口水平和垂直方向大小发生变化时重绘窗口。

hbrBackground设置为(HBRUSH)COLOR_WINDOWFRAME是为了给窗口重绘时指定一个背景色,不然窗口大小改变时,会变成黑色。

消息处理

当消息到达的时候,系统会调用WNDCLASSEXWlpfnWndProc指针所指向的函数来处理消息,这个函数的定义如下:

LRESULT CALLBACK MainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
	switch (msg)
	{
	case WM_DESTROY: 
	{
		PostQuitMessage(0);
		return 0;
	}
	}
	return DefWindowProc(hwnd, msg, wParam, lParam);
}

这是最基础的消息处理,可以选择感兴趣的消息增加对应的case。

错误处理

ErrorExit汇报出错的函数以及系统错误代码,其实现是从检索Last-Error代码复制过来的。

#include <strsafe.h>

void ErrorExit(LPTSTR lpszFunction) 
{ 
    // Retrieve the system error message for the last-error code

    LPVOID lpMsgBuf;
    LPVOID lpDisplayBuf;
    DWORD dw = GetLastError(); 

    FormatMessage(
        FORMAT_MESSAGE_ALLOCATE_BUFFER | 
        FORMAT_MESSAGE_FROM_SYSTEM |
        FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL,
        dw,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        (LPTSTR) &lpMsgBuf,
        0, NULL );

    // Display the error message and exit the process

    lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT, 
        (lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)lpszFunction) + 40) * sizeof(TCHAR)); 
    StringCchPrintf((LPTSTR)lpDisplayBuf, 
        LocalSize(lpDisplayBuf) / sizeof(TCHAR),
        TEXT("%s failed with error %d: %s"), 
        lpszFunction, dw, lpMsgBuf); 
    MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK); 

    LocalFree(lpMsgBuf);
    LocalFree(lpDisplayBuf);
    ExitProcess(dw); 
}

注册窗口

RegisterClassExW函数向系统注册我们的window成功时,我们才能去创建出这个窗口。RegisterClassExW注册成功时返回一个HWND句柄,失败时返回0。

创建窗口及消息分发

hwnd = CreateWindowExW(
		WS_EX_LTRREADING,
		MainClassName,
		L"再见咯",
		WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT,
		CW_USEDEFAULT,
		800,
		600,
		NULL,
		NULL,
		instance,
		NULL
	);
	if (NULL == hwnd) {
		ErrorExit(L"CreateWindowExW");
	}
	ShowWindow(hwnd, showCmd);
	
	while (GetMessageW(&msg, NULL, 0, 0) > 0)
	{
		TranslateMessage(&msg);
		DispatchMessageW(&msg);
	}

通过CreateWindowExW创建我们的窗口,参数的具体意思看官方的文档,这里就不介绍了。在创建成功后通过调用ShowWindow来显示我们的窗口,之后要建立一个while循环来读取系统消息队列里的消息,并通过TranslateMessage来将虚拟密钥消息转换为字符消息,字符消息将发布到调用线程的消息队列,下次线程调用 GetMessageWPeekMessageW 函数时要读取。然后是用DispatchMessageW将消息调度到窗口过程。

最后

至此,一个简单的基本窗口程序就算是成功建立了。全部代码如下:

// main.c

#ifndef UNICODE
#define UNICODE
#endif 
#include<windows.h>
#include<strsafe.h>

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

void ErrorExit(LPTSTR lpszFunction)
{
	// Retrieve the system error message for the last-error code

	LPVOID lpMsgBuf;
	LPVOID lpDisplayBuf;
	DWORD dw = GetLastError();

	FormatMessage(
		FORMAT_MESSAGE_ALLOCATE_BUFFER |
		FORMAT_MESSAGE_FROM_SYSTEM |
		FORMAT_MESSAGE_IGNORE_INSERTS,
		NULL,
		dw,
		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
		(LPTSTR)&lpMsgBuf,
		0, NULL);

	// Display the error message and exit the process

	lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
		(lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)lpszFunction) + 40) * sizeof(TCHAR));
	StringCchPrintf((LPTSTR)lpDisplayBuf,
		LocalSize(lpDisplayBuf) / sizeof(TCHAR),
		TEXT("%s failed with error %d: %s"),
		lpszFunction, dw, lpMsgBuf);
	MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK);

	LocalFree(lpMsgBuf);
	LocalFree(lpDisplayBuf);
	ExitProcess(dw);
}

int WINAPI  WinMain(
	_In_ HINSTANCE instance,
	_In_opt_ HINSTANCE _,
	_In_ LPSTR     cmdLine,
	_In_ int       showCmd
) {
	const wchar_t MainClassName[] = L"Goodbye";
	WNDCLASSEXW mainWndClass;
	MSG msg;
	HWND hwnd;
	memset(&mainWndClass, 0, sizeof(WNDCLASSEXW));
	mainWndClass.hInstance = instance;
	mainWndClass.lpfnWndProc = MainWndProc;
	mainWndClass.cbSize = sizeof(WNDCLASSEXW);
	mainWndClass.lpszClassName = MainClassName;
	mainWndClass.style = CS_HREDRAW|CS_VREDRAW;
	// 没有这一行代码的话,改变窗口大小会变成黑色
	mainWndClass.hbrBackground = (HBRUSH)COLOR_WINDOWFRAME;

	if (0 == RegisterClassExW(&mainWndClass)) {
		ErrorExit(L"RegisterClassExW");
	}
	
	hwnd = CreateWindowExW(
		WS_EX_LTRREADING,
		MainClassName,
		L"再见咯",
		WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT,
		CW_USEDEFAULT,
		800,
		600,
		NULL,
		NULL,
		instance,
		NULL
	);
	if (NULL == hwnd) {
		ErrorExit(L"CreateWindowExW");
	}
	ShowWindow(hwnd, showCmd);
	
	while (GetMessageW(&msg, NULL, 0, 0) > 0)
	{
		TranslateMessage(&msg);
		DispatchMessageW(&msg);
	}
	return (int)msg.wParam;
}

LRESULT CALLBACK MainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
	switch (msg)
	{
	case WM_DESTROY: 
	{
		PostQuitMessage(0);
		return 0;
	}
	}
	return DefWindowProc(hwnd, msg, wParam, lParam);
}
posted @ 2023-05-28 11:05  梅花Q  阅读(323)  评论(0)    收藏  举报