2011-11-25 70 views
1

在Win32 API中,窗口具有指向處理其消息的用戶定義版本的WndProc函數的指針。Win32:更多「面向對象」窗口消息處理系統

有以覆蓋與像MFC消息映射等的溶液這種低級機構一些方法。

在我很小的應用程序,我正在尋找一種方式來封裝用面向對象的解決了這個低層次的東西。

我試圖用HWND鍵和「MyWindowClass」項創建一個C++映射,當我創建MyClass的一個對象時,我向映射添加了一對,然後通過HWN查找MyWindowClass對象。但問題出在CreateWindowEx之後,Win32在內部向剛剛創建的窗口發送了一個WM_CREATE消息,因此我無法在此消息之前在映射中添加對,並且無法通過將WM_CREATE傳遞給對象實例化的WndProc來控制WM_CREATE。

的代碼是:

#ifndef NOTIFYWINDOW_H 
#define NOTIFYWINDOW_H 

#include "Bacn.h" 

class NotifyWindow 
{ 
private: 

    HWND m_hWnd; 

    Gdiplus::Graphics* m_graphics; 

protected: 

    static std::map<HWND, NotifyWindow*> s_NotifyWindows; 

    static LRESULT CALLBACK s_WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); 

    static void s_WndMessageLoop(); 

    LRESULT WndProc(UINT uMsg, WPARAM wParam, LPARAM lParam); 

    void Initialize(); 

    void OnPaint(); 

    void OnCreate(); 

public: 

    NotifyWindow(); 

    ~NotifyWindow(); 
}; 

#endif //NOTIFYWINDOW_H 

及其實施:

#include "NotifyWindow.h" 

using namespace Gdiplus; 
using namespace std; 

map<HWND*, NotifyWindow> NotifyWindow::s_NotifyWindows; 

LRESULT CALLBACK NotifyWindow::s_WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 
{ 
    map<HWND, NotifyWindow*>::iterator search = s_NotifyWindows.find(hWnd); 
    if (search != s_NotifyWindows.end()) 
    { 
     search->second->WndProc(uMsg, wParam, lParam); 
    } 

    return DefWindowProc(hWnd, uMsg, wParam, lParam); 
} 

void NotifyWindow::s_WndMessageLoop() 
{ 

} 

LRESULT NotifyWindow::WndProc(UINT uMsg, WPARAM wParam, LPARAM lParam) 
{ 
    switch (uMsg) 
    { 
    case WM_CREATE: 
     OnCreate(); 
     break; 
    case WM_PAINT: 
     OnPaint(); 
     break; 
    case WM_DESTROY: 
     PostQuitMessage(0); 
     break; 
    case WM_SIZE: 
     break; 
    case WM_SETFOCUS: 
     break; 
    case WM_KILLFOCUS: 
     break; 
    case WM_MOUSEMOVE: 
     break; 
    default: 
     return DefWindowProc(m_hWnd, uMsg, wParam, lParam); 
    } 
    return 0; 
} 

void NotifyWindow::Initialize() 
{ 
    WNDCLASSEX wc; 

    const wchar_t *className = L"BacnNotifyWindowClass"; 
    const wchar_t *windowName = L"BacnNotifyWindow"; 

    HINSTANCE hInstance = GetModuleHandle(NULL); 

    wc.cbSize = sizeof(WNDCLASSEX); 
    wc.lpszClassName = className; 
    wc.lpfnWndProc = NotifyWindow::s_WndProc; 
    wc.hInstance = hInstance; 
    wc.hCursor = LoadCursor(NULL, IDC_ARROW); 
    wc.hIcon = NULL; 
    wc.hIconSm = NULL; 
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); 
    wc.lpszMenuName = NULL; 
    wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; 
    wc.cbClsExtra = 0; 
    wc.cbWndExtra = 0; 

    RegisterClassEx(&wc); 

    DWORD dwExtStyle = WS_EX_TOPMOST; 
    DWORD dwStyle = WS_POPUP | WS_SYSMENU; 

    m_hWnd = CreateWindowEx(
     dwExtStyle, 
     className, 
     windowName, 
     dwStyle, 
     300, 
     300, 
     100, 
     100, 
     NULL, 
     NULL, 
     hInstance, 
     NULL); 

    s_NotifyWindows.insert(pair<HWND, NotifyWindow*>(m_hWnd, this)); 

    ShowWindow(m_hWnd, SW_SHOW); 
} 

NotifyWindow::NotifyWindow() 
{ 
    Initialize(); 
} 

NotifyWindow::~NotifyWindow() 
{ 

} 

void NotifyWindow::OnPaint() 
{ 

} 

void NotifyWindow::OnCreate() 
{ 

} 
+0

的可能重複的具體實施方法,用於存儲該指針在使用的WndProc(http://stackoverflow.com/questions/117792/best-method-for-storing-this-pointer-for-use-in-wndproc ) – legends2k

回答

4

建議:讓WndProc虛擬在窗口基類。請求Windows爲每個窗口實例分配額外內存以存儲指針(使用cbWndExtra)。在創建一個窗口時,使用SetWindowLongPtr將一個指向windows類對象的指針放入與每個窗口實例關聯的這個額外內存中。在您的static sWndProc中,使用GetWindowLongPtr檢索此指針並調用您的窗口基類函數virtual WndProc。在我看來,它比整個額外的地圖對象專門用於調用WndProc調用更爲簡潔。

編輯:另外,你一定要明白,在你的代碼你想每次創建你的窗口類的一個對象時,註冊Windows窗口類?如果你只創建一個窗口,我認爲這在技術上很好,但即使如此,它也是容易出錯的設計。 Windows窗口類應該只註冊一次,而不是每次使用此Windows窗口類創建一個窗口。

編輯:switch條款,在static sWndProc第二時間內第一次在成員函數:同樣,在你的代碼,如果你是不是在你的WndProc處理的Windows消息,你的代碼將調用DefWindowProc兩次此消息。 DefWindowProc不應該在相同的消息上被調用兩次。

編輯:對不起,我錯過了你的實際問題之前不知何故,我認爲你的帖子是關於設計的,不是WM_CREATE。爲了處理WM_NCCREATEWM_NCCALCSIZEWM_CREATE以統一的方式,可以在通話設置lpParamCreateWindowEx來,再次指向你的窗口類的對象。該參數將作爲CREATESTRUCT的成員WM_NCCREATEWM_CREATE傳遞給您的static sWndProc。你可以處理,說,WM_NCCREATE(第一條消息被髮送)內static sWndProc,得到這個指針到你的對象,使用SetWindowLongPtr把它變成窗口實例額外的內存,然後用它來調用成員函數WndProc(只是要小心調用如果您從其構造函數中調用CreateWindowEx,則不是完全創建的Windows類的對象)。這樣,您無需擔心程序中其他任何位置的「低級別」Windows消息派發。當然,您仍然需要您的成員函數WndProc來將消息分派給實際的函數調用。

+0

Borland的VCL框架通過動態分配可執行內存塊來充當WndProc代理函數來解決此問題。目標對象指針存儲在此代理中。當代理被OS調用時,代理會檢索對象指針並調用其上的虛擬方法來處理消息。這樣,即使在收到第一條消息之前對象指針也已準備好(對象指針存儲在HWND中用於其他目的,但不用於消息分派)。消息從OS直接發送到對象而不涉及HWND。 –

+0

@ RemyLebeau-TeamB這是一個很好的處理回調的方法,但在這種情況下是不是有點太重量?它需要調用約定的知識,編譯約定的知識方面,可能的話,虛擬表,使用平臺相關的彙編程序,更多的內存分配與'的VirtualAlloc()'每個對象(幾個指針和彙編指令this指針,至少),並且可能會降低執行速度。 – lapk

+0

另外,在這個特定的案例 - 窗口創建中,我對你將如何從這種技術中受益感到困惑。如果沒有創建每個窗口實例的子類,我無法找到一種方法來使用您的方法 - 您需要爲Windows提供駐留在動態分配的內存中的回調地址,對於每個對象都不同。你需要'HWND'來創建一個窗口實例的子類,這意味着你最早可以在'WM_NCCREATE'中完成。或者你的意思是,有沒有辦法做到這一點,而不需要爲每個窗口實例創建子類?怎麼樣?! – lapk