Windows原生开发

89 阅读7分钟

Windows原生窗口创建全流程(代码+说明)

#include <Windows.h>

// 窗口过程函数声明
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain(
    HINSTANCE hInstance, 
    HINSTANCE hPrevInstance, 
    PSTR szCmdLine, 
    int iCmdShow) 
{
    static TCHAR szAppName[] = TEXT("Win32Window");
    
    // 1. 窗口类注册
    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 = szAppName;
    wcex.hIconSm = LoadIcon(NULL, IDI_WINLOGO);

    if (!RegisterClassEx(&wcex)) {
        MessageBox(NULL, TEXT("Window Registration Failed!"), TEXT("Error"), MB_ICONEXCLAMATION | MB_OK);
        return 0;
    }

    // 2. 创建窗口
    HWND hWnd = CreateWindowEx(
        0,                              // 扩展样式
        szAppName,                     // 窗口类名
        TEXT("Windows原生窗口示例"),    // 窗口标题
        WS_OVERLAPPEDWINDOW,            // 窗口样式
        CW_USEDEFAULT, CW_USEDEFAULT,   // 初始位置
        800,                            // 宽度
        600,                            // 高度
        NULL,                           // 父窗口
        NULL,                           // 菜单
        hInstance,                      // 实例句柄
        NULL                            // 创建参数
    );

    if (!hWnd) {
        MessageBox(NULL, TEXT("Window Creation Failed!"), TEXT("Error"), MB_ICONEXCLAMATION | MB_OK);
        return 0;
    }

    // 3. 显示窗口
    ShowWindow(hWnd, iCmdShow);
    UpdateWindow(hWnd);

    // 4. 消息循环
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return (int)msg.wParam;
}

// 5. 窗口过程函数
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
    switch (message) {
    case WM_PAINT: {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hWnd, &ps);
        
        // 绘制文本
        RECT rect;
        GetClientRect(hWnd, &rect);
        DrawText(hdc, TEXT("Hello Windows!"), 14, &rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
        
        EndPaint(hWnd, &ps);
        break;
    }
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

Windows原生窗口创建关键系统函数详解

1. 窗口类注册

函数/结构体作用关键参数说明
WNDCLASSEX定义窗口类属性- cbSize: 结构体大小
- style: 窗口样式(CS_HREDRAW|CS_VREDRAW)
- lpfnWndProc: 窗口过程函数指针
- hInstance: 应用程序实例句柄
- hbrBackground: 背景画刷(COLOR_WINDOW+1)
- lpszClassName: 注册类名
RegisterClassEx注册窗口类到系统接收WNDCLASSEX*指针,成功返回TRUE,失败返回FALSE
LoadIcon加载图标资源第一个参数为NULL时使用系统默认图标(如IDI_APPLICATION)
LoadCursor加载光标资源使用系统默认箭头光标(IDC_ARROW)

2. 窗口创建与显示

函数作用关键参数说明
CreateWindowEx创建窗口实例- 扩展样式(0为默认)
- 窗口类名(必须已注册)
- 窗口标题
- 窗口样式(WS_OVERLAPPEDWINDOW)
- 初始位置/尺寸(CW_USEDEFAULT表示默认)
- 父窗口句柄(NULL表示无父窗口)
- 菜单句柄(NULL表示无菜单)
ShowWindow控制窗口显示状态iCmdShow参数决定显示方式(SW_SHOW默认显示)
UpdateWindow强制重绘窗口发送WM_PAINT消息到消息队列,触发首次绘制

3. 消息循环系统

函数作用关键参数说明
GetMessage从消息队列获取消息参数:MSG结构体指针,窗口句柄(NULL接收所有窗口消息),消息范围(0,0表示所有消息)
TranslateMessage转换键盘消息将虚拟键消息转换为字符消息(如WM_KEYDOWN→WM_CHAR)
DispatchMessage分发消息到窗口过程将消息发送给目标窗口的WndProc函数处理
// 窗口过程函数,消息的最终处理者
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
    switch (message) {
    case WM_PAINT:
        // 处理绘制消息
        break;
    case WM_DESTROY:
        PostQuitMessage(0); // 发送退出消息,终止消息循环
        break;
    default:
        // 所有未处理的消息,必须交给系统默认处理
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

// WinMain 函数中的主消息循环
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
    // ... 注册窗口类 (WNDCLASS), 创建窗口 (CreateWindow) ...

    MSG msg;
    // 1. GetMessage: 从当前线程的消息队列中阻塞式地获取消息
    //    如果队列为空,线程会在此挂起,不消耗CPU。
    //    如果获取到 WM_QUIT 消息,函数返回 FALSE,循环结束。
    while (GetMessage(&msg, NULL, 0, 0)) {
        
        // 2. TranslateMessage: (可选但推荐) 翻译键盘消息
        //    将 WM_KEYDOWN/WM_KEYUP (虚拟键码) 翻译成 WM_CHAR (字符码)
        //    翻译后的新消息会被重新投递到当前线程的消息队列中。
        TranslateMessage(&msg);

        // 3. DispatchMessage: 分发消息
        //    - 根据 msg.hwnd 找到对应的 WNDPROC (即我们上面定义的 WndProc)
        //    - 调用 WndProc(msg.hwnd, msg.message, msg.wParam, msg.lParam)
        //    - 等待 WndProc 返回
        DispatchMessage(&msg);
    }

    // ... 清理工作 ...
    return (int)msg.wParam;
}

DispatchMessage 的内部原理
当你调用 DispatchMessage(&msg) 时,它做了一件非常简单但致命的事:

  • 它读取 msg.hwnd

  • 它通过 hwnd 查找该窗口对应的 窗口过程函数指针(WNDPROC)

    • 注意:这个指针是在窗口创建时(RegisterClass -> CreateWindow)由应用程序提供的,内核把它保存在 WND 结构里。
  • 它以回调的形式调用这个函数:WndProc(msg.hwnd, msg.message, msg.wParam, msg.lParam)

4. 窗口过程函数

函数作用关键参数说明
WndProc处理窗口消息- message: 消息类型(如WM_PAINT/WM_DESTROY)
- wParam/lParam: 消息附加参数
DefWindowProc默认消息处理未处理的消息由系统进行默认处理
PostQuitMessage发送退出消息退出消息循环时调用,设置MSG的wParam为退出码

Windows共享内存操作全指南(代码+说明)

核心函数与参数说明

函数名作用关键参数说明所属头文件
CreateFileMapping创建内存映射对象hFile:文件句柄(INVALID_HANDLE_VALUE表示使用页文件)
flProtect:内存保护属性(如PAGE_READWRITE)
lpName:共享内存名称(跨进程标识)
<windows.h>
OpenFileMapping打开已存在的内存映射对象dwDesiredAccess:访问模式(FILE_MAP_READ/WRITE)
lpName:共享内存名称
<windows.h>
MapViewOfFile映射内存到进程地址空间hFileMappingObject:映射对象句柄
dwDesiredAccess:访问模式
dwNumberOfBytesToMap:映射字节数
<windows.h>
UnmapViewOfFile解除内存映射lpBaseAddress:映射视图地址<windows.h>
CloseHandle关闭对象句柄hObject:要关闭的句柄<windows.h>

完整代码示例

#include <windows.h>
#include <iostream>
#include <cassert>

int main() {
    const char* sharedMemName = "Global\\MySharedMemory";
    const SIZE_T sharedMemSize = 1024; // 1KB

    // 创建共享内存
    HANDLE hMapping = CreateFileMapping(
        INVALID_HANDLE_VALUE,    // 使用系统页文件
        NULL,                    // 默认安全属性
        PAGE_READWRITE,          // 可读写
        0,                       // 高位文件大小
        sharedMemSize,           // 低位文件大小
        sharedMemName            // 共享内存名称
    );

    if (!hMapping) {
        std::cerr << "创建共享内存失败!错误代码:" << GetLastError() << std::endl;
        return 1;
    }

    // 映射到进程地址空间
    LPVOID pSharedMem = MapViewOfFile(
        hMapping,               // 映射对象句柄
        FILE_MAP_ALL_ACCESS,    // 完全访问权限
        0,                      // 偏移高位
        0,                      // 偏移低位
        0                       // 映射整个区域
    );

    if (!pSharedMem) {
        std::cerr << "映射共享内存失败!错误代码:" << GetLastError() << std::endl;
        CloseHandle(hMapping);
        return 1;
    }

    // 写入数据到共享内存
    int* pData = static_cast<int*>(pSharedMem);
    pData[0] = 42; // 写入示例数据
    std::cout << "写入共享内存的值:" << pData[0] << std::endl;

    // 解除映射
    if (!UnmapViewOfFile(pSharedMem)) {
        std::cerr << "解除映射失败!错误代码:" << GetLastError() << std::endl;
    }

    // 关闭句柄
    CloseHandle(hMapping);

    // 示例:另一个进程打开已存在的共享内存
    HANDLE hExistingMapping = OpenFileMapping(
        FILE_MAP_ALL_ACCESS,    // 完全访问权限
        FALSE,                  // 不可继承
        sharedMemName           // 共享内存名称
    );

    if (hExistingMapping) {
        LPVOID pExistingMem = MapViewOfFile(
            hExistingMapping,
            FILE_MAP_ALL_ACCESS,
            0,
            0,
            0
        );

        if (pExistingMem) {
            int* pExistingData = static_cast<int*>(pExistingMem);
            std::cout << "从共享内存读取的值:" << pExistingData[0] << std::endl;
            assert(pExistingData[0] == 42); // 验证数据一致性

            UnmapViewOfFile(pExistingMem);
        }
        CloseHandle(hExistingMapping);
    } else {
        std::cerr << "打开共享内存失败!错误代码:" << GetLastError() << std::endl;
    }

    return 0;
}

使用内核对象实现全局单例

#include <windows.h>
#include <iostream>

const wchar_t WINDOW_CLASS_NAME[] = L"MyAppWindowClass";
const wchar_t WINDOW_TITLE[] = L"MyApp";
const wchar_t MUTEX_NAME[] = L"Global\\MyAppMutex";

HWND g_hWnd = NULL;

// 激活已有实例
bool ActivateExistingInstance()
{
    HWND hwnd = FindWindow(WINDOW_CLASS_NAME, WINDOW_TITLE);
    if (!hwnd)
        return false;
    
    SendMessage(hwnd, WM_USER + 100, 0, 0);
    return true;
}

// 窗口过程
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
    case WM_USER + 100:
        std::wcout << L"收到激活通知" << std::endl;
        
        if (IsIconic(hwnd))
            ShowWindow(hwnd, SW_RESTORE);
        
        SetForegroundWindow(hwnd);
        FlashWindow(hwnd, TRUE);
        return 0;
        
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }
    
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

int main()
{
    // 检测重复实例
    HANDLE hMutex = CreateMutex(NULL, FALSE, MUTEX_NAME);
    
    if (GetLastError() == ERROR_ALREADY_EXISTS)
    {
        std::wcout << L"程序已存在,激活它..." << std::endl;
        
        if (ActivateExistingInstance())
        {
            std::wcout << L"激活成功" << std::endl;
        }
        else
        {
            std::wcout << L"未找到窗口" << std::endl;
        }
        
        CloseHandle(hMutex);
        return 1;
    }
    
    // 注册窗口类
    WNDCLASSEX wc = {0};
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.lpfnWndProc = WindowProc;
    wc.hInstance = GetModuleHandle(NULL);
    wc.lpszClassName = WINDOW_CLASS_NAME;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    
    RegisterClassEx(&wc);
    
    // 创建窗口
    g_hWnd = CreateWindowEx(
        0, WINDOW_CLASS_NAME, WINDOW_TITLE,
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, 800, 600,
        NULL, NULL, GetModuleHandle(NULL), NULL
    );
    
    if (!g_hWnd)
    {
        CloseHandle(hMutex);
        return 1;
    }
    
    ShowWindow(g_hWnd, SW_SHOW);
    UpdateWindow(g_hWnd);
    
    std::wcout << L"程序运行中..." << std::endl;
    
    // 消息循环
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    
    CloseHandle(hMutex);
    return 0;
}