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

Window消息循环中*wm->actStkSize意外变为66的问题求助

问题:未手动修改的*wm->actStkSize值在DispatchMessage后莫名变为66

我在Window_manager.c文件中从未手动修改*wm->actStkSize的值,但在RunMessageLoop函数调用DispatchMessage(&msg)后,该值从0变为66,完全不清楚原因。调用GetMessageW时传入wm->hwnd也无法改变这一现象。

相关代码

main.c

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
    WindowManager wm;
    InitWindowManager(&wm);
    printf("from main: %d %d\n", *wm.actBfrSize, *wm.actStkSize);

    if (CreateAndShowWindow(&wm, hInstance, nCmdShow) != 0)
    {
        MessageBox(NULL, L"Failed to create window.", L"Error", MB_OK | MB_ICONERROR);
        return 1;
    }

    int result = RunMessageLoop(&wm);
    CleanupWindowManager(&wm);

    return result;
}

Window_manager.c

#include "window_manager.h"
#include <stdio.h>
#include <windows.h>
#include <windowsx.h>
#include "config.h"
#include "graphics.h"

void InitWindowManager(WindowManager *wm) {
    wm->hwnd = NULL;
    wm->logFile = NULL;

    Document doc;
    Page pg;
    RawPoint** stack = (RawPoint**)malloc(sizeof(RawPoint*));
    RawPoint* buffer = (RawPoint*)malloc(SIZE_OF_BUFFER * sizeof(RawPoint));
    *stack = buffer;

    pg.rawStk = stack;
    pg.rawBfrSize = 0;
    pg.rawStkSize = 0;
    doc.page = &pg;
    wm->document = &doc;
    wm->actBuffer = buffer;
    wm->actPt = buffer;
    wm->actBfrSize = &pg.rawBfrSize;
    wm->actStkSize = &pg.rawStkSize;

    // Initialize penData attributes
    wm->penData.position.x = 0;
    wm->penData.position.y = 0;
    wm->penData.pressure = 0.0f;
    wm->penData.azimuth = 0.0f;
    wm->penData.altitude = 0.0f;
    wm->penData.tiltX = 0.0f;
    wm->penData.tiltY = 0.0f;
    wm->penData.twist = 0.0f;
    wm->penData.touching = FALSE;
    wm->penData.buttonPressed = FALSE;

    wcscpy_s(wm->displayText, MAX_TEXT_LENGTH, L"Pointer Events: None");
    wcscpy_s(wm->logFileName, MAX_TEXT_LENGTH, L"logs/pen_pressure_log.txt");
    // Open the log file for writing
    _wfopen_s(&wm->logFile, wm->logFileName, L"w");
}

int CreateAndShowWindow(WindowManager *wm, HINSTANCE hInstance, int nCmdShow) {
    
    // Register window class
    WNDCLASSW wc = {};
    wc.lpfnWndProc = WindowProc;
    wc.hInstance = hInstance;
    wc.lpszClassName = L"MyWindowClass";

    if (RegisterClassW(&wc) == 0) {
        return 1;
    }
    // Create window
    wm->hwnd = CreateWindowExW(0, L"MyWindowClass", L"My Window Title",
        WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
        500, 300, NULL, NULL, hInstance, wm);
    HWND hButton = CreateWindowExW(0, L"BUTTON", L"redraw",
        WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
        10, 50, 100, 30,
        wm->hwnd, (HMENU)1001, GetModuleHandle(NULL), NULL);
    if (wm->hwnd == NULL) {
        return 1;
    }
    ShowWindow(wm->hwnd, nCmdShow);
    return 0;
}

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    WindowManager *wm;
    if (uMsg == WM_NCCREATE) {
        CREATESTRUCT *pCreate = (CREATESTRUCT *)lParam;
        wm = (WindowManager *)pCreate->lpCreateParams;
        SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)wm);
        wm->hwnd = hwnd;
        printf("from ProcIf: %d %d\n", *(wm->actBfrSize), *(wm->actStkSize));
    } else {
        wm = (WindowManager *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
    }
    switch (uMsg) {
        case WM_CLOSE: {
            printf("from WM_CLOSE: %d %d\n", *(wm->actBfrSize), *(wm->actStkSize));
            DestroyWindow(hwnd);
            break;
        }
        case WM_DESTROY: {
            printf("from WM_DESTROY: %d %d\n", *(wm->actBfrSize), *(wm->actStkSize));
            PostQuitMessage(0);
            break;
        }
        case WM_POINTERDOWN: {
            printf("from WM_POINTERDOWN: %d %d\n", *(wm->actBfrSize), *(wm->actStkSize));
            wm->penData.touching = TRUE;
            break;
        }
        case WM_POINTERUP: {
            printf("from WM_POINTERDOWN: %d %d\n", *(wm->actBfrSize), *(wm->actStkSize));
            wm->penData.touching = FALSE;
            break;
        }
        case WM_POINTERUPDATE: {
            printf("from WM_POINTERUPDATE: %d %d\n", *(wm->actBfrSize), *(wm->actStkSize));
            wm->penData.position.x = GET_X_LPARAM(lParam);
            wm->penData.position.y = GET_Y_LPARAM(lParam);
            ScreenToClient(wm->hwnd, &wm->penData.position);
            wm->actPt->coords.x = wm->penData.position.x;
            wm->actPt->coords.y = wm->penData.position.y;
            wm->actPt->touching = wm->penData.touching;
            wm->actPt->pressure = GET_POINTERID_WPARAM(wParam) / 5551.0f;
            UpdateDisplay(wm);
            InvalidateRect(hwnd, NULL, TRUE);
            
            (*wm->actBfrSize)++;
            if (*wm->actBfrSize == SIZE_OF_BUFFER) {
                printf("Stoooooooooop!!!");
            }
            else {
                wm->actPt++;
            }
            break;
        }
        case WM_SIZE: {
            printf("from WM_SIZE: %d %d\n", *(wm->actBfrSize), *(wm->actStkSize));
            // Do Some Stuff
            break;}
        case WM_PAINT: {
            printf("from WM_PAINT: %d %d\n", *(wm->actBfrSize), *(wm->actStkSize));
            break;}
        case WM_COMMAND: {
            printf("from WM_COMMAND: %d %d\n", *(wm->actBfrSize), *(wm->actStkSize));
        }
        default: {
            return DefWindowProc(hwnd, uMsg, wParam, lParam);
        }
    }
    return 0;
}

int RunMessageLoop(WindowManager *wm) {
    MSG msg;
    while (GetMessageW(&msg, NULL, 0, 0))
    {
        printf("from beginLoop: %d %d\n", *(wm->actBfrSize), *(wm->actStkSize));
        TranslateMessage(&msg);
        printf("from midLoop:   %d %d\n", *(wm->actBfrSize), *(wm->actStkSize));
        DispatchMessage(&msg);
        printf("from endLoop:   %d %d\n", *(wm->actBfrSize), *(wm->actStkSize));
    }
    return (int)msg.wParam;
}

void CleanupWindowManager(WindowManager *wm) {
    if (wm->logFile != NULL) {
        fclose(wm->logFile);
    }
}

程序输出

from main: 0 0
from ProcIf: 0 0
from WM_SIZE: 0 0
from beginLoop: 0 0
from midLoop:   0 0
from endLoop:   0 66
from beginLoop: 0 66
from midLoop:   0 66
from endLoop:   0 66

问题原因与解决方案

原因分析

问题出在InitWindowManager函数中的局部变量生命周期:

  • 函数内定义的Document doc和Page pg是栈上的局部变量,当InitWindowManager执行完毕返回后,这些变量占用的栈内存会被系统回收并可能被后续函数调用(比如消息循环相关的系统函数)覆盖。
  • wm->actStkSize指向的是pg.rawStkSize的地址,也就是这块已经失效的栈内存地址,后续任何对栈内存的操作都会导致该地址的值被意外修改,这就是为什么*wm->actStkSize会莫名变成66。

解决方案

有两种常见修复方式:

  1. 动态分配内存:将Document和Page改为堆内存分配,确保它们的生命周期覆盖整个程序运行过程:

    void InitWindowManager(WindowManager *wm) {
        // ... 其他初始化代码 ...
        Document* doc = (Document*)malloc(sizeof(Document));
        Page* pg = (Page*)malloc(sizeof(Page));
        RawPoint** stack = (RawPoint**)malloc(sizeof(RawPoint*));
        RawPoint* buffer = (RawPoint*)malloc(SIZE_OF_BUFFER * sizeof(RawPoint));
        *stack = buffer;
    
        pg->rawStk = stack;
        pg->rawBfrSize = 0;
        pg->rawStkSize = 0;
        doc->page = pg;
        wm->document = doc;
        wm->actBfrSize = &pg->rawBfrSize;
        wm->actStkSize = &pg->rawStkSize;
        // ... 其他初始化代码 ...
    }
    

    注意:需要在CleanupWindowManager中添加对应的内存释放逻辑,避免内存泄漏。

  2. 调整变量作用域:将Document和Page作为WindowManager结构体的成员变量,或者放在全局作用域,确保它们的生命周期不早于wm变量。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 17:34:58