You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Windows GUI窗口最小化时崩溃问题排查(附代码)

问题分析与修复方案

你的程序崩溃主要有三个核心问题:

1. limit()函数未实现

代码中多次调用limit()来钳位坐标,但完全没有定义这个函数,这会导致未定义行为(编译报错或运行时崩溃)。必须先实现这个钳位函数,确保坐标落在合法范围内。

2. 重复定义draw_rect函数

代码里出现了两个完全相同的draw_rect函数定义,这会触发编译错误,需要删除其中一个。

3. 窗口最小化时缓冲区内存为NULL

当窗口最小化,GetClientRect返回的客户区宽高为0,此时buffer_memory_size = 0 * 0 * sizeof(unsigned int) = 0,调用VirtualAlloc申请0字节内存会返回NULL。后续绘制函数访问NULL指针直接导致崩溃。需要在绘制前判断宽高是否合法,避免无效绘制。


修复后的完整代码

#include <windows.h>
#include <Memoryapi.h>
#include <stdio.h>
#pragma comment(lib, "Gdi32.lib")
#pragma comment(lib, "onecore.lib")

//global variables
bool run = true;
int width, height, buffer_memory_size;
void *memory;
BITMAPINFO BMINFO;

// 实现钳位函数:将value限制在min和max之间
int limit(int min, int max, int value)
{
    if (value < min) return min;
    if (value > max) return max;
    return value;
}

//function used to color background
void render_background()
{
    // 宽高为0时直接返回,避免访问NULL
    if (width <= 0 || height <= 0 || memory == NULL)
        return;
        
    unsigned int *ptr = (unsigned int *)memory;

    for (int y = 0; y < height; y++)
    {
        for (int x = 0; x < width; x++)
        {
            *ptr = 0xff5500;
            ptr++;
        }
    }
}

//function used to draw rectangle
void draw_rect(int x0, int y0, int x1, int y1, unsigned int color)
{   
    // 宽高为0或内存为空时直接返回
    if (width <= 0 || height <= 0 || memory == NULL)
        return;
        
    // 先确保y0 <= y1,避免循环不执行或反向遍历
    int y_start = limit(0, height - 1, min(y0, y1));
    int y_end = limit(0, height - 1, max(y0, y1));
    
    for (int y = y_start; y <= y_end; y++)
    {
        unsigned int *ptr = (unsigned int *)memory + y * width;

        // 同样确保x0 <= x1
        int x_start = limit(0, width - 1, min(x0, x1));
        int x_end = limit(0, width - 1, max(x0, x1));
        
        for (int x = x_start; x <= x_end; x++)
        {
            ptr[x] = color;
        }
    }
}

LRESULT CALLBACK MainWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
    case WM_DESTROY:
        run = false;
        return 0;

    case WM_SIZE:
        //obtain the new size of the window and the memory size of new needed buffer
        RECT R;
        GetClientRect(hwnd,&R);

        width = R.right - R.left;
        height = R.bottom - R.top;
        buffer_memory_size = width*height*sizeof(unsigned int); //buffer memory size in terms of bytes

        //allocate memory in the heap for new buffer
        if (memory != NULL)
        {
            VirtualFree(memory,0,MEM_RELEASE);
            memory = NULL; // 释放后置空,避免野指针
        }

        // 只有宽高都大于0时才申请内存
        if (width > 0 && height > 0)
        {
            memory = VirtualAlloc(0, buffer_memory_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
        }

        //update BMINFO with updated class members
        BMINFO.bmiHeader.biSize = sizeof(BMINFO.bmiHeader);
        BMINFO.bmiHeader.biWidth = width;
        BMINFO.bmiHeader.biHeight = height;
        BMINFO.bmiHeader.biPlanes = 1;
        BMINFO.bmiHeader.biBitCount = 32;
        BMINFO.bmiHeader.biCompression = BI_RGB;

        return 0;
    }
    
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
    //create window class
    const wchar_t name[] = L"window class";

    WNDCLASS wc = {};

    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpszClassName = name;
    wc.lpfnWndProc = MainWndProc;

    //register window class
    RegisterClass(&wc);

    //create window and display window
    HWND H = CreateWindowExW(0, name, L"First Window", WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, 1280, 720, 0, 0, hInstance, 0);
    HDC hdc = GetDC(H);


    //game loop
    while (run)
    {   
        MSG msg;

        //input
        while (PeekMessage(&msg, H, 0, 0, PM_REMOVE))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }

        //logic
        render_background();

        //drawing rectangle x0 = 400, x1 = 700, y0 = 300, y1 = 500  
        draw_rect(400,300,700,500,0xff00ff);


        //rendering:只有宽高合法且内存不为空时才绘制
        if (width > 0 && height > 0 && memory != NULL)
        {
            StretchDIBits(hdc,0,0,width,height,0,0,width,height,memory,&BMINFO,DIB_RGB_COLORS,SRCCOPY);
        }
    }

    // 程序退出前释放内存
    if (memory != NULL)
    {
        VirtualFree(memory, 0, MEM_RELEASE);
    }
    ReleaseDC(H, hdc);

    return 0;
}

关键修复点说明

  1. 实现limit()函数:完成坐标钳位逻辑,确保所有绘制的x/y坐标都落在[0, width-1]和[0, height-1]范围内。
  2. 删除重复的draw_rect定义:解决编译错误。
  3. 空指针与零尺寸判断:在render_background、draw_rect、StretchDIBits调用前,都先检查width/height是否大于0,以及memory是否为NULL,避免无效内存访问。
  4. 处理坐标顺序:在draw_rect中先确保y0 <= y1和x0 <= x1,避免因传入反向坐标导致的异常循环。
  5. 释放内存后置空指针:避免野指针问题,确保后续判断准确。
  6. 程序退出时清理资源:释放内存和设备上下文,避免资源泄漏。

内容的提问来源于stack exchange,提问作者Sliferslacker

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 05:44:52