2016-05-13 203 views
0

在我的項目中,我有2個類:'窗口'和'上下文'。 類'窗口'使用WinAPI實例化一個窗口,一切工作正常。 例如此代碼的工作原理如下:類已經存在

Window win("Hello,", 600, 400); 
Window win2("World!", 600, 400); 

'Context'類創建一個OpenGL 4.0上下文。爲了做到這一點, 它需要創建一個臨時窗口和臨時上下文來檢索所有OpenGL 4.0指針,這需要創建一個OpenGL 4.0上下文。 例如此代碼也適用:

Window win("Context", 600, 400); 
Context ctx(win); 

一切工作正常。

但是,我正在重構代碼。由於一個窗口根本只有一個上下文,我決定,窗口應該管理上下文,因此它應該實例化並自己刪除它。但是這導致我發生奇怪的錯誤。

// Constructor of window 
// We're at the end of the constructor 
if(true == bOWnContext) 
    pContext = internal::Context(*this); 
}; // Constructor ends here 

當執行應用程序時,我得到一個異常/錯誤:「類已經註冊。」 這就奇怪了,因爲下面的代碼對我的作品:

Window winA(...); 
Window winB(...); 
// or 
Context ctx(winA); 
// This works like a charm 

一些collegue的建議,我應該只有一次註冊窗口類, 所以我嘗試在構造函數如下:

static bool bRegistered = false; 
    if(false == bRegistered){ 
    WNDCLASSEX wc  = {}; 
    wc.cbSize   = sizeof(WNDCLASSEX); 
    wc.hInstance  = GetModuleHandle(nullptr); 
    wc.lpfnWndProc  = internal::WndProc; 
    wc.lpszClassName = pTitle; 
    wc.style   = CS_VREDRAW | CS_HREDRAW | CS_OWNDC; 

    if(0 == RegisterClassEx(&wc)){ 
     // 
     // The exception class will contain the error code and 
     // a error discription. 
     // 
     std::error_code err_code(GetLastError(), std::system_category()); 
     throw std::system_error(err_code); 
    } 
    bRegistered = true; 
    } 

然而,這給了我另一個錯誤「找不到窗口類」。

有人可以啓發我,爲什麼我的代碼不工作?

+1

你的窗口類沒有類名。它不能被引用。不知道你在調用CreateWindow [Ex]'時提供的窗口類名稱。 GetModuleHandle(nullptr)'是一個等待發生的錯誤,只要將代碼編譯成DLL即可。無論如何,您提供的代碼不是類註冊失敗的地方,並帶有*「已註冊」*錯誤消息。 – IInspectable

+0

窗口類名稱等於我在構造函數中提供的窗口的名稱。現在我明白爲什麼出現'找不到窗口類'的錯誤。 GetModuleHandle(nullptr)有什麼問題?我已經編譯成一個DLL,它的工作原理。唯一的失敗是,如果我在另一個窗口內創建一個新窗口。 – Julien

+1

哦,你說得對,你提供的是一個班級名稱。 'GetModuleHandle(nullptr)'有什麼問題:它將模塊句柄返回給EXE,而不是DLL。但它是實現窗口過程的DLL。包羅萬象的解決方案,找到正確的模塊句柄:從靜態庫訪問當前模塊的HINSTANCE(https://blogs.msdn.microsoft.com/oldnewthing/20041025-00/?p=37483)。 – IInspectable

回答

0

所以問題是,該類被多次註冊。我對這個問題的第一個解決方案是試圖引用一個可能不存在的類。 我的工作解決方案創建一個類,並記住它的參考方式如下:

static const char* pcClassName; 
    if(nullptr == pcClassName){ 
     WNDCLASSEX wc  = {}; 
     wc.cbSize   = sizeof(WNDCLASSEX); 
     wc.hInstance  = ((HINSTANCE)&__ImageBase); 
     wc.lpfnWndProc  = internal::WndProc; 
     wc.lpszClassName = pTitle; 
     wc.style   = CS_VREDRAW | CS_HREDRAW | CS_OWNDC; 

     if(0 == RegisterClassEx(&wc)){ 
      // 
      // The exception class will contain the error code and 
      // a error description. 
      // 
      std::error_code err_code(GetLastError(), std::system_category()); 
      throw std::system_error(err_code); 
     } 
     pcClassName = pTitle; 
    } 

類的名稱用於以後參考窗口類中實際創建窗口。例如。像這樣:

pHandle->handle = CreateWindowEx(
     bFullscreen ? WS_EX_APPWINDOW : NULL, 
     pcClassName, 
     pTitle, 
     bFullscreen ? WS_POPUP : WS_OVERLAPPEDWINDOW, // NOTE: Perhaps I'll create a window config struct? 
     GetSystemMetrics(SM_CXSCREEN)/2, 
     GetSystemMetrics(SM_CYSCREEN)/2, 
     clientArea.right - clientArea.left, 
     clientArea.bottom - clientArea.top, 
     nullptr, // We're not having a parent window 
     nullptr, // nor a win32 menu. 
     ((HINSTANCE)&__ImageBase), 
     pHandle // Is needed for the router 'WndProc'. 
    ); 

然而,由於我編譯此代碼到一個DLL,我用取回權「HINSTANCE」 @IInspectable解決方案。 欲瞭解更多詳情,請查看他的評論。

__ImageBase定義如下:

EXTERN_C IMAGE_DOS_HEADER __ImageBase; 
1

你開始一個錯誤的假設:

… Since a window can have only one context at all, …

都能跟得上。

可以有上下文的,事實上上下文沒有綁定到特定窗口的任意數目。

只要窗口的像素格式相匹配的背景下,你可以使窗口上下文電流。如果您有多個線程,您可以同時在同一個窗口(在上下文與窗口的像素格式兼容的情況下)在不同的上下文中顯示不同的上下文。

+0

哦,那可能是一個。設計的問題,我想到了一個上下文綁直流 – Julien

+0

@Julien:它不過是有可能的設備上下文的任意數量的每一個窗口 – IInspectable

+0

是不是有每個窗口只有一個,考慮到定義窗口類「。 ?CS_OWNDC「 – Julien