Search code examples
cwindowswindowmessage-handlers

Window Message Handling functions somehow change Value of int variable which should remain untouched


In my Window_manager.c file I nowhere edit the Value of *wm->actStkSize by hand (!?), however after calling DispatchMessage(&msg); in the RunMessageLoop function, the Value somehow changed from 0 to 66, I have absolutely no idea how.

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
    // Register window class
    WNDCLASSW wc = {}; // Note the use of WNDCLASSW for wide strings
    wc.lpfnWndProc = WindowProc;
    wc.hInstance = hInstance;
    wc.lpszClassName = L"MyWindowClass"; // Use wide-character string

    if (RegisterClassW(&wc) == 0) {
        // Handle registration failure
        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) {
        // Retrieve the pointer to the WindowManager from the CREATESTRUCT
        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));

        // printf("%s",wm->displayText);
        // printf("if");
    } else {
        wm = (WindowManager *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
        // printf("from ProcElse: %d %d\n", *(wm->actBfrSize), *(wm->actStkSize));
    }
    // printf("from Proc: %d %d\n", *(wm->actBfrSize), *(wm->actStkSize));
    // printf("WindowPrc: %i\n", *wm->actStkSize);
    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; // Pen is down
            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));
            // Handle pointer events
            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); // Request a redraw of the window
            
            (*wm->actBfrSize)++;
            if (*wm->actBfrSize == SIZE_OF_BUFFER) {
                printf("Stoooooooooop!!!");
            }
            else {
                // printf("actStkSize: %i\n", *(wm->actStkSize));
                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));
            // Do some Stuff
            break;}
        case WM_COMMAND: {
            printf("from WM_COMMAND: %d %d\n", *(wm->actBfrSize), *(wm->actStkSize));
            //Do some Stuff
        }
        default: {
            // printf("from default: %d %d\n", *(wm->actBfrSize), *(wm->actStkSize));
            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) {
    // Close the log file if it's open
    if (wm->logFile != NULL) {
        fclose(wm->logFile);
    }
}

This is the output:

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

Passing wm->hwnd when calling GetMessageW doesn't change anything.


Solution

  • The doc and pg local variables of InitWindowManager are being accessed via pointers after the function returns. The lifetime of the variables has already expired by that point, leading to undefined behavior.

    To fix the problem, wm->document and wm->document->page can be pointed to dynamically allocated memory as follows:

        Document* doc = malloc(sizeof(*doc));
        Page* pg = malloc(sizeof(*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;