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
是为了给窗口重绘时指定一个背景色,不然窗口大小改变时,会变成黑色。
消息处理
当消息到达的时候,系统会调用WNDCLASSEXW
的lpfnWndProc
指针所指向的函数来处理消息,这个函数的定义如下:
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
来将虚拟密钥消息转换为字符消息,字符消息将发布到调用线程的消息队列,下次线程调用 GetMessageW
或PeekMessageW
函数时要读取。然后是用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); }
本文作者:梅花Q
本文链接:https://www.cnblogs.com/ReginaQ/p/17437933.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步