项目创建
在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);
}
标签:mainWndClass,界面,hwnd,win32,开发,lpDisplayBuf,msg,窗口,NULL
From: https://www.cnblogs.com/ReginaQ/p/17437933.html