Проблема с доступом к полям после первого в структуре в C с использованием WinApi

Недавно я изучал API Win32 для языка C, следуя стандартному руководству на сайте Microsoft (вот этот).

При попытке реализовать то, чему учит руководство, у меня возникла проблема с доступом к полям в пользовательском интерфейсе. STATEINFO struct, в частности, каждый раз, когда я пытаюсь получить доступ к полю, которое не является первым в объявлении структуры в функции WindowProcess, моя программа компилируется без проблем, но во время выполнения она вылетает с ошибкой 0xC000041D. Для справки: мой компилятор — MinGW, а IDE — CodeBlocks. Я пробовал отладку, но не могу осознать эту странную ошибку, мне никогда не приходило в голову, что я не могу получить доступ к какому-либо полю после первого.

Вот упрощенная версия моего кода:

#include <stdlib.h>
#include <stdio.h>
#include <windows.h>

LRESULT CALLBACK WindowProcess(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

typedef struct {UINT lastMessage1; UINT lastMessage2;} STATEINFO;

STATEINFO* GetAppState(HWND hwnd);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR pCmdLine, int nCmdShow) {
    // creating the window class
    const wchar_t WindowClassName[] = L"Test Window";

    WNDCLASS wc = {};
    wc.lpfnWndProc      = WindowProcess;
    wc.hInstance        = hInstance;
    wc.lpszClassName    = WindowClassName;

    RegisterClass(&wc);

    STATEINFO stateInfo = {}, *pStateInfo = &stateInfo;

    // creating the window instance
    HWND hwnd = CreateWindowEx(
        0, WindowClassName, "Window Title", WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
        NULL, NULL, hInstance, pStateInfo);
    ShowWindow(hwnd, nCmdShow);

    // starting the message loop of the window
    MSG msg = {};
    while (GetMessage(&msg, NULL, 0, 0) > 0) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
}

LRESULT CALLBACK WindowProcess(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {

    STATEINFO *pStateInfo = malloc(sizeof(STATEINFO*));

    if (uMsg == WM_CREATE) {
        pStateInfo = (STATEINFO*)(*((CREATESTRUCT*)(lParam))).lpCreateParams;
        SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pStateInfo);
    } else {
        pStateInfo = GetAppState(hwnd);
    }
    switch (uMsg) {
        case WM_CLOSE: {
            DestroyWindow(hwnd);
            return 0;
        }
        case WM_DESTROY: {
            PostQuitMessage(0);
            return 0;
        }
        break;
    }
    // here is the problem:
    pStateInfo->lastMessage1 = 1; // this is fine
    pStateInfo->lastMessage2 = 1; // this is NOT fine, if this line is present the program crashes
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

STATEINFO* GetAppState(HWND hwnd)
{
    LONG_PTR ptr = GetWindowLongPtr(hwnd, GWLP_USERDATA);
    STATEINFO *pStateInfo = (STATEINFO*)(ptr);
    return pStateInfo;
}

Проблема возникает всякий раз, когда я пытаюсь записать второй элемент в структуре STATEINFO, может быть, это какая-то ошибка, связанная с преобразованием типа указателя от void* к STATEINFO*? Я пробовал отладку, но кажется, что единственное, что действительно приводит к сбою кода, - это строка

pStateInfo->lastMessage2 = 1;

Доступ к полю не вызывает проблемы, но анализ выражений с его помощью вызывает.

Я понятия не имею, почему, и не могу найти ничего полезного для отладки в учебниках по Windows или по другим вопросам на этом сайте.

Если у кого-то есть какие-либо предложения по решению этой проблемы, я готов узнать все, что смогу, из ответов, и если понадобится что-то еще, я с радостью предоставлю дополнительную информацию в комментариях.

Имейте в виду, что я не опытный программист, особенно с Win32 API, о котором я только пытаюсь узнать, и что мне могут не хватать некоторых аспектов теории C. Кроме того, это мой первый вопрос о StackOverflow, поэтому, если у вас есть какие-либо советы о том, как лучше формулировать вопросы, я буду рад узнать.

Спасибо всем заранее!

🤔 А знаете ли вы, что...
C широко используется в разработке операционных систем, включая UNIX и Linux.


74
1

Ответ:

Решено

проблема в том, как вы выделяете память для структуры STATEINFO и как обрабатываете указатели.

  1. Вы выделяете память для указателя на STATEINFO вместо выделения память для фактической структуры STATEINFO. Это вызывает неопределенность поведение при доступе к полям структуры.
  2. вы правильно установили и получили GWLP_USERDATA для хранения и получить указатель на вашу структуру STATEINFO?!
  3. память для STATEINFO при обработке сообщения WM_CREATE и сохранения указатель с помощью SetWindowLongPtr ???
  4. избегайте утечек памяти при уничтожении окна !!???
#include <stdlib.h>
#include <stdio.h>
#include <windows.h>

LRESULT CALLBACK WindowProcess(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

typedef struct {
    UINT lastMessage1;
    UINT lastMessage2;
} STATEINFO;

STATEINFO* GetAppState(HWND hwnd);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR pCmdLine, int nCmdShow) {
    // creating the window class
    const wchar_t WindowClassName[] = L"Test Window";

    WNDCLASS wc = {};
    wc.lpfnWndProc      = WindowProcess;
    wc.hInstance        = hInstance;
    wc.lpszClassName    = WindowClassName;

    RegisterClass(&wc);

    STATEINFO stateInfo = {}, *pStateInfo = &stateInfo;

    // creating the window instance
    HWND hwnd = CreateWindowEx(
        0, WindowClassName, L"Window Title", WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
        NULL, NULL, hInstance, pStateInfo);
    ShowWindow(hwnd, nCmdShow);

    // starting the message loop of the window
    MSG msg = {};
    while (GetMessage(&msg, NULL, 0, 0) > 0) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return 0;
}

LRESULT CALLBACK WindowProcess(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    STATEINFO *pStateInfo = NULL;

    if (uMsg == WM_CREATE) {
        CREATESTRUCT *pCreate = (CREATESTRUCT*)lParam;
        pStateInfo = (STATEINFO*)pCreate->lpCreateParams;
        SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pStateInfo);
    } else {
        pStateInfo = GetAppState(hwnd);
    }

    if (pStateInfo != NULL) {
        switch (uMsg) {
            case WM_CLOSE:
                DestroyWindow(hwnd);
                return 0;

            case WM_DESTROY:
                PostQuitMessage(0);
                return 0;

            default:
                // This is where you update the state info
                pStateInfo->lastMessage1 = 1; // This is fine
                pStateInfo->lastMessage2 = 1; // This should now be fine
                break;
        }
    }

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

STATEINFO* GetAppState(HWND hwnd) {
    return (STATEINFO*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
}