2010-06-26 24 views
3

我一直在使用C++和Win32(非MFC/ATL)我正在編寫我自己的類庫以包裝某些Win32對象(特別是HWND)。什麼是有效的方式來包裝HWND在C++中的對象?

當談到創建窗口,我發現「RegisterClassEx/CreateWindowEx」方法非常尷尬。這種設計使得編寫簡單的類包裝很困難(必須使用thunk,TLS或其他複雜的機制)。

在我看來,讓應用程序在窗口創建時指定窗口過程和用戶​​數據指針會更簡單。

是否有任何明顯的原因,我錯過了這裏的設計選擇?有沒有一個非常簡單而有效的方法來實現它?

回答

7

ATL的CWindow和CWindowImpl是你的朋友。

CWindowImpl可以處理您所說的RegisterClass/CreateWindow尷尬。

CWindow是HWND的一個基本的「包裝」類,所有的win32函數都被抽象出來。

我比較喜歡使用MFC的原因。 ATL是一個非常輕量級的類,提供了所有的源代碼。這是一個簡單的#include,沒有額外的庫或運行時間來處理。在滾動我自己的WndProcs和窗口封裝類多年之後,我發現CWindowImpl很開心。你必須在你的代碼中聲明一個全局的AtlModuleExe實例來使用它,但除此之外,ATL保持不變。

鏈接documenation下面這些類: CWindow的: http://msdn.microsoft.com/en-us/library/d19y607d.aspx

的CWindowImpl: http://msdn.microsoft.com/en-us/library/h4616bh2.aspx

更新:這是我挖了你一些示例代碼:

class CMyApp : public CAtlExeModuleT<CMyApp> 
{ 
public: 
    static HRESULT InitializeCom() 
    { 
     CoInitialize(NULL); 
     return S_OK; 
    } 
}; 

CMyApp g_app; 

class CMyWindow : public CWindowImpl<CMyWindow> 
{ 
public: 
    CMyWindow(); 
    ~CMyWindow(); 
    BEGIN_MSG_MAP(CMyWindow) 
     MESSAGE_HANDLER(WM_PAINT, OnPaint); 
     MESSAGE_HANDLER(WM_CLOSE, OnClose); 
    END_MSG_MAP(); 

    LRESULT OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& fHandled); 
    LRESULT OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& fHandled); 
}; 

LRESULT CMyWindow::OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& fHandled) 
{ 
    // your WM_PAINT code goes here 
} 

LRESULT CMyWindow::OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& fHandled) 
{ 
    PostQuitMessage(); 
} 

int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR cmdline, int nCmdShow) 
{ 
    // 640x480 window 
    CMyWindow appwindow; 
    RECT rect = {0, 0, 640, 480}; 
    RECT rectActual = {0}; 
    appwindow.Create(NULL, rect, L"App Window", WS_OVERLAPPEDWINDOW); 
    appwindow.ShowWindow(SW_SHOW); 

    { 
     MSG msg; 
     while (GetMessage(&msg, 0, 0, 0)) 
     { 
      TranslateMessage(&msg); 
      DispatchMessage(&msg); 
     } 
    } 
    // app shutdown 

    appwindow.DestroyWindow(); 

    return 0; 
} 
+0

ATL的[WTL擴展](http://wtl.sourceforge.net/)也很有用,因爲它們添加了更多控件,RAII封裝了GDI對象,框架窗口等等。 – holtavolt 2015-05-28 02:28:54

1

訴諸thunk或tls?在這種情況下,我不知道thunk是什麼意思,但它很容易 - 如果只是有點複雜 - 將一個窗口引導到一個C++類包裝器中。

class UserWindow 
{ 
    HWND _hwnd; 
public: 
    operator HWND(){ 
    return _hwnd; 
    } 
    UserWindow():_hwnd(0){} 
    ~UserWindow(){ 
    if(_hwnd){ 
     SetWindowLongPtr(GWL_USERDATA,0); 
     DestroyWindow(_hwnd); 
    } 
    static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam){ 
    UserWindow* self = 0; 
    if(uMsg == WM_CREATE) 
    { 
     LPCREATESTRUCT crst = (LPCREATESTRUCT)lParam; 
     self = (Window*)crst->lpCreateParams; 
     SetWindowLongPtr(hwnd,GWL_USERDATA,(LONG_PTR)self); 
     self->_hwnd = hwnd; 
    } 
    else 
     self = (Window*)GetWindowLongPtr(hwnd,GWL_USERDATA); 

    if(self){ 
     LRESULT lr = self->WndProc(uMsg,wParam,lParam); 
     if(uMsg == WM_DESTROY){ 
     if(self = (Window*)GetWindowLongPtr(hwnd,GWL_USERDATA)) 
      self->_hwnd = NULL; 
     } 
     return lr; 
    } 
    return DefWindowProc(hwnd,uMsg,wParam,lParam); 
    } 
    HWND Create(int x, int y, int w, int h, LPCTSTR pszTitle,DWORD dwStyle,DWORD dwStyleEx,LPCTSTR pszMenu,HINSTANCE hInstance, HWND hwndParent){ 
    WNDCLASSEX wcex = { sizeof (wcex),0}; 
    if(!GetClassInfo(hInstance,ClassName(),&wcex)){ 
     wcex.style = CS_HREDRAW | CS_VREDRAW; 
     wcex.lpfnWndProc = WindowndProc; 
     wcex.cbClsExtra = 0; 
     wcex.cbWndExtra = 0; 
     wcex.hInstance = hInstance; 
     wcex.lpszClassName = ClassName(); 
     OnCreatingClass(wcex); 
    RegisterClassEx(&wcex); 
    } 
    return CreateWindowEx(dwStyleEx, ClassName(), pszTitle, dwStyle, x, y, w, h, hwndParent, pszMenu, hInstance, this); 
    } 
    // Functions to override 
    virtual LPCTSTR ClassName(){ 
    return TEXT("USERWINDOW"); 
    } 
    virtual LRESULT WindowProc(UINT uMsg, WPARAM wParam,LPARAM lParam){ 
    return DefWindowProc(uMsg,wParam,lParam); 
    } 
    virtual void Window::OnCreatingClass(WNDCLASSEX& wcex){ 
    wcex.hCursor = LoadCursor(NULL,IDC_ARROW); 
    } 
}; 

這一切都有點複雜,但這意味着窗口可以通過刪除類或通過銷燬來安全銷燬。在WM_CREATE將GWL_USERDATA設置爲「this」之前,在調用CreateWindow期間發送一個或兩個調整大小相關的消息,但實際上它們沒有任何影響。窗口類在第一次實例化時自動創建。


一件事在第一次調用此式自動類註冊的創建不支持是這種類型的窗口作爲一個對話框控件的實例 - 爲了支持這種情況下的事情都需要整體轉換被改變...提供一個靜態類註冊函數...在靜態WM_CREATE處理程序中的「新MyClass」...它對我來說不是顯而易見的,這可以以框架類型的方式完成。

+0

兩個主要的失敗:首先,Get/SetWindowLong應該是Get/SetWindowLongPtr,否則你不能編譯64位。其次,SetWindowLong/Ptr有一些模糊的功能,你需要在它發生效果之前調用它。 http://msdn.microsoft.com/en-us/library/ms644898(VS.85).aspx – Puppy 2010-06-26 21:03:17

+0

1.我從內存中寫道,所以預期錯字。但是正確的,我應該使用功能的Ptr形式。我記得它在其他地方;-P 2.是的,沒有。 SetWindowLong可以影響窗口的許多方面,其中許多窗口只能在窗口創建時才真正讀取 - 例如非客戶端框架樣式位。這些樣式需要使用標誌中的SWP_FRAMECHANGED調用SetWindowPos,以使窗口重新生成新的幀。 GWL_USERDATA「立即生效」 - 因爲效果是確保GetWindowLong檢索值。 – 2010-06-26 22:04:24

相關問題