Странная ошибка с WndProc и DispatchMessage.Функции-члены не существуют? - PullRequest
0 голосов
/ 14 октября 2011

Так что немного странная ошибка происходит при получении сообщения WM_KEYDOWN в моем классе Window.

У меня есть функция Global WndProc, которая определяет экземпляр окна и отправляет сообщение в свою локальную функцию WndProc.

//Every Windows Message will hit this function. 
LRESULT CALLBACK GlobalWndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {

    //Get the Window Instance from the userdata
    Window* targetWindow = (Window*)GetWindowLongPtr(hwnd, GWLP_USERDATA);

    //If no window exists, then it must be the first time so we should set it
    if (!targetWindow) {
        //First let's try and extract the Window instance pointer from the lparam
        CREATESTRUCT* createStruct = (CREATESTRUCT*)lparam;
        if (createStruct) {
            targetWindow = (Window*)createStruct->lpCreateParams;
            SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)targetWindow);
        }
        else {
            //It was some other message which we just can't deal with right now
            return DefWindowProc(hwnd, msg, wparam, lparam);
        }
    }

    //Now we can pipe it to the Window's local wnd proc function
    return targetWindow->LocalWndProc(hwnd, msg, wparam, lparam);
}

А мой локальный wndproc выглядит так:

LRESULT CALLBACK Window::LocalWndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
    switch (msg) {

    case WM_KEYDOWN:
        ToggleFullScreen(!m_fullScreen);
        return 0;
        break;

    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
        break;

    case WM_CLOSE:
        PostQuitMessage(0);
        return 0;
        break;

    default:
        return DefWindowProc(hwnd, msg, wparam, lparam);
    }
}

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

Теперь по какой-то причине, когда я запускаю это и нажимаю любую клавишу на клавиатуре, я получаю ошибку исключения:

Необработанное исключение на 0x77c015ee в Athena_Debug.exe: 0xC0000005: Местоположение чтения нарушения прав доступа 0x0000012d.

С CallStack:

ntdll.dll!77c015ee()    
ntdll.dll!77bf015e()    
user32.dll!7588788a()   

Athena_Debug.exe! Athena :: Window :: HandleWindowsMessage () Строка 195 + 0xc байт C ++ Athena_Debug.exe! Athena :: AthenaCore :: StartEngine () Строка 96 + 0x12 байт C ++ Athena_Debug.exe! WinMain (HINSTANCE__ * hInstance, HINSTANCE__ * hPrevInstance, char * lpCmdLine, int nCmdShow) Строка 44 C ++ Athena_Debug.exe! __TmainCRTStartup () Строка 547 + 0x2c байт C Athena_Debug.exe! WinMainCRTStartup () Строка 371 C kernel32.dll! 7702339a ()

Строка, на которой она фактически разбивается, это строка DispatchMessage (& msg), расположенная здесь:

MSG Window::HandleWindowsMessage() {
    MSG msg;
    ZeroMemory(&msg, sizeof(MSG));
    if (m_realTime) {
        PeekMessage(&msg, m_hwnd, 0, 0, PM_REMOVE);
    }
    else {
        GetMessage(&msg, m_hwnd, 0, 0);
    }

    if (msg.message != WM_NULL) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return msg;
}

Так что интересно то, что если я закомментирую ToggleFullScreen и вместо этого добавлю туда OutputDebugTrace, он будет работать нормально и выведет трассировку отладки. Похоже, он не может разрешить функцию для экземпляра? Если я закомментирую все в ToggleFullScreen, он все равно вылетает. Если я создаю совершенно новую функцию, которая ничего не делает, и вызывает ее в ответ на WM_KEYDOWN, она все равно выдает ошибку.

У кого-нибудь есть идеи, почему это происходит, или какие-то идеи о том, как я могу это исправить?

Спасибо!

Ответы [ 3 ]

2 голосов
/ 14 октября 2011

Первое сообщение не обязательно должно быть WM_NCCREATE (или WM_CREATE), вы должны быть готовы получить WM_SIZE или WM_GETMINMAXINFO (и, вероятно, другие сообщения) перед любым сообщением создания!

В вашем коде вы изменилиif (createStruct) { до if (WM_NCCREATE==msg && createStruct) { ...

Вы можете увидеть это в программе Рэймонда Чена .

2 голосов
/ 14 октября 2011

Ваш обратный вызов WindowProc должен выглядеть примерно так:

//Every Windows Message will hit this function.
LRESULT CALLBACK GlobalWndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
    Window* targetWindow;

    if (msg == WM_CREATE)
    {
        //extract the Window instance pointer from the lparam
        CREATESTRUCT* createStruct = (CREATESTRUCT*)lparam;

        targetWindow = (Window*)createStruct->lpCreateParams;
        SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)targetWindow);
    }
    else
    {
        //Get the Window Instance from the userdata
        targetWindow = (Window*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
    }

    if (targetWindow)
    {
        //Now we can pipe it to the Window's local wnd proc function
        return targetWindow->LocalWndProc(hwnd, msg, wparam, lparam);
    }

    //It was some other message which we just can't deal with right now
    return DefWindowProc(hwnd, msg, wparam, lparam);
} 
2 голосов
/ 14 октября 2011

Я бы предположил, что у вас неверный указатель, полученный из GetWindowLongPtr, так как он не был установлен SetWindowLongPtr. Как я написал в своем комментарии, вы не должны проверять, является ли он пустым (я понятия не имею, что там по умолчанию, но я бы ничего не предполагал.), Но устанавливайте его, когда вы получаете WM_CRERATE (который гарантированно будет первым сообщением). отправлено в окно). Вы проверяли, действительно ли SetWindowLongPtr вызывается?

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

EDIT:

Другая возможная причина: почему вы не используете значение результата из PeekMessage? я вижу, что вы тестируете WM_NULL, который должен быть 0 в данный момент (вы обнуляете память), но PeekMessage не гарантирует, что он не касается структуры MSG, даже если он ничего не извлекает. Использование PeekMessage результат должен использоваться всегда. Плюс обнуление памяти довольно неэффективно на данный момент.

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