2010-06-26 62 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

相关问题