代码之家  ›  专栏  ›  技术社区  ›  Martin

什么是在C++中封装HWNDS的有效方法?

  •  3
  • Martin  · 技术社区  · 16 年前

    我一直在用C++和Win32(非MFC/ATL)来工作,我自己编写自己的类库来包装某些Win32对象(特别是HWNDS)。

    当涉及到创建窗口时,我发现“registerClassEx/createWindowEx”方法非常笨拙。这种设计使得编写简单的类包装器变得困难(必须使用thunks、tls或其他复杂的机制)。

    在我看来,让应用程序在窗口创建时指定窗口过程和用户数据指针会更简单。

    有没有明显的原因让我错过了这里的设计选择?有没有一个真正简单有效的方法来工作?

    2 回复  |  直到 16 年前
        1
  •  7
  •   selbie    16 年前

    ATL的Cwindows和CWindowImpl是你的朋友。

    CWindowImpl makes takes care of the RegisterClass/CreateWindow awkwardness that you speak of.

    CWindow is a basic "wrapper" class for an HWND with all the win32 functions abstracted out.

    我喜欢ATL而不是MFC的原因。ATL是一组非常轻量的类,提供了所有源代码。这是一个简单的包括没有额外的库或运行时来处理。在我自己的wndprocs和窗口封装类滚动多年后,我发现cwindowimpl很高兴与之合作。您必须在代码中声明一个全局atlmodulexe实例才能使用它,但除此之外,atl不会妨碍您的工作。

    Links to documenation for these classes below: 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;
    }
    
        2
  •  1
  •   Chris Becke    16 年前

    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);
      }
    };
    

    这一切有点复杂,但这意味着可以通过删除类或被销毁来安全地销毁窗口。在调用CreateWindow期间,在wm_create将gwl_userdata设置为“this”之前,有一个或两个与大小相关的消息发送,但实际上它们没有任何影响。窗口类在第一次实例化窗口时自动创建。


    在第一次调用创建时不支持这种类型的自动类注册是将这种类型的窗口实例化为对话框上的控件——以支持这种情况,需要改变一大堆事物…提供静态类注册函数…一个“新MyC类”在静态WMLCub处理程序中…我不清楚这是如何以一种框架式的方式来完成的。