注册窗口

分为用户窗口和系统窗口

用户窗口

多种多样的,需要提前注册,需要填充一个数据结构(窗口清单)

首先声明一个Windows API主函数

int APIENTRY WinMain(
    HINSTANCE hinstance,  // handle to current instance 
    HINSTANCE hinstPrev,  // handle to previous instance 
    LPSTR lpCmdLine,      // address of command-line string 
    int nCmdShow)         // show-window type
{
    return 0
}

创建过程

注册窗口

WNDCLASS wc;

// 注册窗口
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = (WNDPROC)MainWndProc; //回调函数
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hinstance;

wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = HBRUSH(GetStockObject(WHITE_BRUSH));

wc.lpszMenuName = L"MainMenu";
wc.lpszClassName = L"MainWindowClass";   //类的名字

RegisterClass(&wc);

回调函数这里需要注册一个类来接收回调

LRESULT MainWndProc(HWND, UINT, WPARAM, LPARAM){
    return 0;
};

创建窗口

注意,这里的创建窗口只是在内核中创建窗口对象(Window Object)并返回句柄(HWND),并不会真的渲染窗口

// 创建窗口
// 一旦成功,窗口并不会显示出来,而是会存在数据缓存区
HWND hwnd=CreateWindow(L"MainWindowClass",L"窗口", WS_OVERLAPPEDWINDOW,50,50,500,500,NULL,NULL, hinstance,NULL);
//设置hwnd拿到窗口的内存

渲染窗口

//渲染窗口
ShowWindow(hwnd,SW_SHOW);
UpdateWindow(hwnd);

处理消息

此时窗口会一闪而过,所以我们需要消息循环,持续刷新

MSG msg;
while(GetMessage(&msg,0,0,0)){
    TranslateMessage(&msg);
    DispatchMessageA(&msg);
}

此时需要操作系统处理我们不处理的消息,更改回调函数

LRESULT MainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lparam) {
    return DefWindowProc(hwnd, msg, wParam, lparam);
};
消息
概念

Windows 操作系统是 事件驱动(Event-Driven) 的。当用户触发输入(如点击鼠标、按下键盘)或系统状态发生改变(如窗口大小调整、定时器到期)时,操作系统会将这些事件封装为**“消息”(Message) ,投递给对应的窗口线程。

结构体
typedef struct tagMSG {
    HWND   hwnd;     // 1. 窗口句柄:指定该消息属于哪一个具体窗口
    UINT   message;  // 2. 消息 ID:一个无符号整数,代表具体事件(如 WM_PAINT, WM_LBUTTONDOWN)
    WPARAM wParam;   // 3. 附加参数1:32位/64位整型,通常传递简短数据(如按键的虚拟键码)
    LPARAM lParam;   // 4. 附加参数2:32位/64位整型,通常传递复杂数据(如鼠标点击的 X/Y 坐标,或内存指针)
    DWORD  time;     // 5. 时间戳:消息被创建时的时间
    POINT  pt;       // 6. 光标坐标:消息创建时,鼠标在屏幕上的绝对坐标 (x, y)
} MSG;
消息的作用与生命周期

消息的存在实现了操作系统与应用程序、以及应用程序各模块之间的解耦。其核心作用体现在消息循环的三个关键步骤中:

  1. GetMessage(获取消息): 从当前线程的消息队列(Message Queue)中阻塞式地取出一条消息。如果队列为空,线程挂起,释放 CPU 资源。
  2. TranslateMessage(翻译消息): 专门用于处理键盘输入。它将按键消息(如 WM_KEYDOWN 和 WM_KEYUP)组合翻译成字符消息(WM_CHAR),并重新投递回消息队列。
  3. DispatchMessage(分发消息): 这是最核心的一步。操作系统根据 MSG.hwnd 找到该窗口在注册时绑定的回调函数(lpfnWndProc) ,并直接调用该函数,将 hwnd, message, wParam, lParam 作为参数传入。
扩展消息

我们可以扩展消息回调函数的情况来针对不同消息展示不同的功能

LRESULT MainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lparam) {

    switch(msg){
    case WM_CREATE:
        MessageBox(0,0,0,0);
        Break;
    }
    return DefWindowProc(hwnd, msg, wParam, lparam);//  去执行操作系统默认的函数
};

此时就需要更改我们的分发消息函数

MSG msg;
while(GetMessage(&msg,0,0,0)){
    TranslateMessage(&msg);
    // DispatchMessageA(&msg);
    WinProc(msg.)
}

所有代码

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <windows.h>
#include<WinBase.h>

LRESULT MainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lparam) {
    switch (msg) {
    case WM_CREATE:
        MessageBox(0, 0, 0, 0);
        break;
    }
    return DefWindowProc(hwnd, msg, wParam, lparam);//去执行操作系统默认的函数
};

int APIENTRY WinMain(
    HINSTANCE hinstance,  // handle to current instance 
    HINSTANCE hinstPrev,  // handle to previous instance 
    LPSTR lpCmdLine,      // address of command-line string 
    int nCmdShow)         // show-window type 
{
    WNDCLASS wc;

    // 注册窗口
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = (WNDPROC)MainWndProc; //回调函数
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hinstance;

    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = HBRUSH(GetStockObject(WHITE_BRUSH));

    wc.lpszMenuName = L"MainMenu";
    wc.lpszClassName = L"MainWindowClass";   //类的名字

    RegisterClass(&wc);

    // 创建窗口
    // 一旦成功,窗口并不会显示出来,而是会存在数据缓存区
    HWND hwnd = CreateWindow(L"MainWindowClass",L"窗口", WS_OVERLAPPEDWINDOW,50,50,500,500,NULL,NULL, hinstance,NULL);

    //渲染窗口
    ShowWindow(hwnd,SW_SHOW);
    UpdateWindow(hwnd);
 
    MSG msg;
    while (GetMessage(&msg, 0, 0, 0))
    {
        TranslateMessage(&msg);
        // DispatchMessageW(&msg);
        MainWndProc(msg.hwnd,msg.message,msg.wParam,msg.lParam);
    }
}

扩展风格

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <windows.h>
#include<WinBase.h>

// 透明风格
LRESULT MainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lparam) {
    switch (msg) {
    case WM_CREATE:
        MessageBox(0, 0, 0, 0);
        break;
    }
    return DefWindowProc(hwnd, msg, wParam, lparam);//去执行操作系统默认的函数
};

int APIENTRY WinMain(
    HINSTANCE hinstance,  // handle to current instance 
    HINSTANCE hinstPrev,  // handle to previous instance 
    LPSTR lpCmdLine,      // address of command-line string 
    int nCmdShow)         // show-window type 
{
    WNDCLASSEX wc = { 0 };//这里初始化一下

    // 注册窗口
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = (WNDPROC)MainWndProc; //回调函数
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hinstance;

    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = HBRUSH(RGB(0,0,0));

    wc.lpszMenuName = L"MainMenu";
    wc.lpszClassName = L"MainWindowClass";   //类的名字

    wc.cbSize = sizeof(WNDCLASSEX);

    RegisterClassEx(&wc);//这里声明扩展风格

    // 创建窗口
    // 一旦成功,窗口并不会显示出来,而是会存在数据缓存区
    HWND hwnd = CreateWindowEx(WS_EX_LAYERED, L"MainWindowClass", L"窗口", WS_POPUP, 50, 50, 500, 500, NULL, NULL, hinstance, NULL);

    //设置分层窗口属性
    SetLayeredWindowAttributes(hwnd, RGB(0,0,0),0 , LWA_COLORKEY);

    //渲染窗口
    ShowWindow(hwnd, SW_SHOW);
    UpdateWindow(hwnd);

    MSG msg;
    while (GetMessage(&msg, 0, 0, 0))
    {
        TranslateMessage(&msg);
        // DispatchMessageW(&msg);
        MainWndProc(msg.hwnd, msg.message, msg.wParam, msg.lParam);
    }
}