2013-03-18 144 views
0

我一直在寫一個win32包裝類,並且遇到了一個問題:因爲每個類的實例都有一個窗口,所以我使用SetWindowLongPtrW()this指針包含在用戶信息空間中,允許我從靜態的WndProc函數中調用消息處理函數。這工作正常:我可以調用該函數。但是,當我嘗試從消息處理程序調用另一個成員函數時,我在0x00000088 處遇到訪問衝突,它會進行編譯。 我發佈了很多,因爲說實話我不太確定問題來自哪裏... 請隨時對我的代碼進行評論/批評。謝謝您的幫助!C++,WIN32,WndProc到成員函數崩潰(訪問衝突在0x88)

這裏是標題:

#pragma once 
#include <Windows.h> 
#include "GlobalDefines.h" 
#include "GraphicsWrapper.h" 
#include "Keyboard.h" 

namespace Startup 
{ 

class GraphicsWrapper; 

class WindowsWrapper 
{ 
public: 
    WindowsWrapper(HINSTANCE hInstance, 
        HINSTANCE hPrevInstance, 
        LPSTR lpCmdLine, 
        INT nCmdShow); 
    ~WindowsWrapper(); 
    void EnterMsgLoop(GraphicsWrapper* Gfx); 
    static LRESULT _stdcall WindowProc(HWND hWnd, 
             UINT message, 
             WPARAM wParam, 
             LPARAM lParam); 
    LRESULT _stdcall MessageHandler(HWND hWnd, 
            UINT message, 
            WPARAM wParam, 
            LPARAM lParam); 
    WNDCLASSEX WndClass; 
    MSG Message; 
    RECT Desktop; 
    RECT Taskbar; 
    RECT WindowCoordinates; 
    LPSTR CommandLineArgs; 
    INT CmdShow; 
    HINSTANCE TheInstance; 
    HWND WindowHandle; 

    void InitializeWndClassEx(); 
    void InitializeWindowHandleHWND(); 
    void ShowWindowOnScreen(); 
    bool GetScreenRect(RECT & Desktop); 
    bool GetTaskbarRect(RECT& rectTaskbar); 
    bool GetWindowCoords(RECT& WindowCoordinates); 
    int GetTaskbarSide(); 
    enum TaskbarSides 
    { 
     Top, 
     Right, 
     Bottom, 
     Left 
    }; 
    void SetFullScreen(bool Enable); 
}; 

static IO::Keyboard * kbd; 
} 

這是實施的相關部分。我會標記崩潰發生的位置。

void Startup::WindowsWrapper::InitializeWndClassEx() 
{ 
    WndClass.hIcon = LoadIcon(TheInstance,(MAKEINTRESOURCE(IDI_MAIN_ICON))); 
    WndClass.hIconSm = LoadIcon(TheInstance,(MAKEINTRESOURCE(IDI_MAIN_ICON))); 
    WndClass.cbSize = sizeof(WNDCLASSEX); 
    WndClass.style = CS_HREDRAW | CS_VREDRAW; 
    WndClass.lpfnWndProc = WindowProc; 
    WndClass.hInstance = TheInstance; 
    WndClass.hCursor = LoadCursor(NULL, IDC_ARROW); 
    WndClass.lpszClassName = L"WindowClassName"; 
    RegisterClassEx(&WndClass); 
    SetWindowLongPtrW(WindowHandle, GWLP_USERDATA, (long)this); 
} 

void Startup::WindowsWrapper::SetFullScreen(bool Enable) 
{ 

    long style = Enable ? WS_POPUP : WS_OVERLAPPED | WS_SYSMENU; 
    static RECT windowRect = {}; 
    static bool needRect = true; 

    if (needRect) 
    { 
     GetWindowRect(WindowHandle, &windowRect); 
     needRect = false; 
    } 

    SetWindowLong(WindowHandle, GWL_STYLE, style); 

    if (Enable) 
    { 
     SetWindowPos(WindowHandle, HWND_TOPMOST, 
        0,0, 
        GetSystemMetrics(SM_CXSCREEN), 
        GetSystemMetrics(SM_CYSCREEN), 
        SWP_SHOWWINDOW); 
    } 
    else 
    { 
     SetWindowPos(WindowHandle, 0, 
        windowRect.left,windowRect.top, 
        windowRect.right - windowRect.left, 
        windowRect.bottom - windowRect.top, 
        SWP_SHOWWINDOW); 
    } 
} 

LRESULT CALLBACK Startup::WindowsWrapper::WindowProc 
(
    HWND hWnd, 
    UINT message, 
    WPARAM wParam, 
    LPARAM lParam 
) 
{ 
    WindowsWrapper* ourObjectPtr = NULL; 
    long thisObject = GetWindowLongW(hWnd, GWLP_USERDATA); 

    ourObjectPtr = (WindowsWrapper *)((void*)thisObject); 

    long Result = ourObjectPtr->MessageHandler(hWnd, message, wParam, lParam); 
    RET(Result); 

} 

LRESULT _stdcall Startup::WindowsWrapper::MessageHandler 
(
    HWND hWnd, 
    UINT message, 
    WPARAM wParam, 
    LPARAM lParam 
) 
{ 
    switch(message) 
    { 
     case WM_DESTROY: 
      PostQuitMessage(0); 
      break; 
     case WM_KEYDOWN: 
      switch(wParam) 
     { 
      case VK_ESCAPE: 
       PostQuitMessage(0); //Works fine here, but... 
       break; 
      case VK_SPACE: 
       this->SetFullScreen(false); //Crashes here w/ access violation 
       break; 
      case VK_SHIFT: 
       this->SetFullScreen(true); //Or here, w/ the same error. 
       break; 
     }   
    } 
    return DefWindowProc(hWnd, message, wParam, lParam); 
} 

這是createWindowEx電話。再次感謝您的幫助。

void Startup::WindowsWrapper::InitializeWindowHandleHWND() 
{ 
WindowHandle = CreateWindowEx(NULL, 
    L"WindowClassName", 
      L"WindowTitle" 
    WS_OVERLAPPED | WS_SYSMENU, 
    WindowCoordinates.left, WindowCoordinates.top, 
    WindowCoordinates.right, WindowCoordinates.bottom, 
    NULL, NULL, TheInstance, 
    CommandLineArgs); 
} 
+1

訪問這樣的小內存地址通常意味着指向某個對象的指針是NULL。 – 2013-03-18 02:32:44

+0

CreateWindow調用在哪裏?爲什麼你的SetWindowLong立即進行課後註冊?當時'WindowHandle'的價值是什麼,你有沒有打算去調試那個點? – WhozCraig 2013-03-18 02:38:27

+2

當您檢索窗口數據時,您應該使用'GetWindowLongPtr',而不是'GetWindowLong',並且使用'DWORD_PTR'而不是'long'',否則它幾乎肯定會在x64構建中失敗。 – 2013-03-18 02:41:35

回答

1

我有一些來自自定義對話框處理程序的代碼我寫了一段時間回來,這可能對您有用。

同樣的原理適用於窗口,但切換WM_INITDIALOGWM_CREATE並且還用GWLP_USERDATA替換DWLP_USER。回調的格式也很微妙。你應該可以挽救幾乎所有的這個功能。

LRESULT CALLBACK CDialog::DlgProc(HWND hWndDlg, UINT msg, WPARAM wParam, LPARAM lParam) 
{ 
    CDialog* pWindow; 

    if(msg == WM_INITDIALOG) { 
     SetWindowLongPtr(hWndDlg, DWLP_USER, (LONG_PTR)lParam); 
     pWindow = reinterpret_cast<CDialog*>(lParam); 
     pWindow->m_hWnd = hWndDlg; 
    } else { 
     pWindow = reinterpret_cast<CDialog*>((LPARAM)GetWindowLongPtr(hWndDlg, DWLP_USER)); 
    } 

    if(pWindow != NULL) { 
     LRESULT ret = pWindow->OnMessage(msg, wParam, lParam); 
     if(msg == WM_NCDESTROY) pWindow->m_hWnd = NULL; 
    } 

    return FALSE; 
}