Недавно я изучал 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, поэтому, если у вас есть какие-либо советы о том, как лучше формулировать вопросы, я буду рад узнать.
Спасибо всем заранее!
Мой профессор заставил нас работать намного хуже, поэтому я подумал, что это приемлемо, лол. Я запомню это на будущее.
проблема в том, как вы выделяете память для структуры STATEINFO и как обрабатываете указатели.
#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, что приводило к ошибке сегментирования программы при попытке доступа к значениям, на которые она ссылается. Еще раз спасибо!
Я не говорю, что это проблема, но это страшилка для будущего читателя вашего кода:
pStateInfo = (STATEINFO*)(*((CREATESTRUCT*)(lParam))).lpCreateParams
- проблема в большом количестве приведений, которые необходимо исследовать на каждом уровне. Предпочитаю не делать приведения.