Проблема с доступом к полям после первого в структуре в 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, поэтому, если у вас есть какие-либо советы о том, как лучше формулировать вопросы, я буду рад узнать.

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

Я не говорю, что это проблема, но это страшилка для будущего читателя вашего кода: pStateInfo = (STATEINFO*)(*((CREATESTRUCT*)(lParam))).lpCreateParams - проблема в большом количестве приведений, которые необходимо исследовать на каждом уровне. Предпочитаю не делать приведения.

Ted Lyngmo 06.08.2024 01:50

Мой профессор заставил нас работать намного хуже, поэтому я подумал, что это приемлемо, лол. Я запомню это на будущее.

Leonardo Carofiglio 06.08.2024 11:09
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
2
74
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 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);
}

Хорошо, теперь я понял проблему, спасибо за вашу помощь! Итак, дело в том, что изначально pStateInfo никогда не был установлен, и поэтому GetWindowLongPtr всегда возвращал 0, что приводило к ошибке сегментирования программы при попытке доступа к значениям, на которые она ссылается. Еще раз спасибо!

Leonardo Carofiglio 06.08.2024 11:15

Другие вопросы по теме