天天看点

一个基于Win32的完全面向对象的窗口类

  记得以前看过一本书,叫做《windows图形编程》,作者是Yuan Feng,机械工业出版社出版的,该书从Win32API开始介绍,讲述了Win32 GDI和DirectDraw API的技术细节,里面好多技术都研究得特别深入。最让我难忘的是它基本上用Win32API完成了一套类似于MFC的图形框架,不过与MFC不同的是,MFC里面消息映射机制还不算是完全的面向对象的,而它则完全用面向对象的技术方法实现了图形库。

  最近上网查资料,偶然发现他最基础的那个窗口基类居然能在网上找到代码。真是喜出往外了。

  看看他的用面向对象技术封装的KWindow类吧。呵呵,我就喜欢在类名前加个大写字母K了,真是与我爱好一样啊。

  头文件KWindow.h:

  1. #if _MSC_VER > 1000
  2. #pragma once
  3. #endif // _MSC_VER > 1000
  4. // 以K开头来命名类,是希望与MFC有明显区别
  5. class KWindow
  6. {
  7. protected:
  8.     // 处理WM_PAINT消息,由WndProc调用
  9.     virtual void OnDraw(HDC hDC)
  10.     {
  11.     }
  12.     // 处理WM_KEYDOWN消息,由WndProc调用
  13.     virtual void OnKeyDown(WPARAM wParam, LPARAM lParam)
  14.     {
  15.     }
  16.     // 真正的消息分发/处理函数
  17.     virtual LRESULT WndProc(HWND hWnd, UINT uMsg,
  18.         WPARAM wParam, LPARAM lParam);
  19.     // API中注册的消息处理函数,不能是成员函数,因为成员函数有this指针
  20.     static LRESULT CALLBACK WindowProc(HWND hWnd,
  21.         UINT uMsg, WPARAM wParam, LPARAM lParam);
  22.     // 派生类可以在这里修改窗口的属性,如图标、菜单等
  23.     virtual void GetWndClassEx(WNDCLASSEX & wc);
  24. public:
  25.     // 保存该窗口对应的HWND
  26.     HWND  m_hWnd;
  27.     // m_hWnd 由CreateEx成员函数调用API函数CreateWindowEx赋值
  28.     KWindow(void)
  29.     {
  30.         m_hWnd = NULL;
  31.     }
  32.     // destructor
  33.     virtual ~KWindow(void)
  34.     {
  35.     }
  36.     // 调用API函数CreateWindowEx创建窗口
  37.     virtual bool CreateEx(DWORD dwExStyle,
  38.         LPCTSTR lpszClass, LPCTSTR lpszName, DWORD dwStyle,
  39.         int x, int y, int nWidth, int nHeight, HWND hParent,
  40.         HMENU hMenu, HINSTANCE hInst);
  41.     // 注册窗口
  42.     bool RegisterClass(LPCTSTR lpszClass, HINSTANCE hInst);
  43.     // 消息循环
  44.     virtual WPARAM MessageLoop(void);
  45.     BOOL ShowWindow(int nCmdShow) const
  46.     {
  47.         return ::ShowWindow(m_hWnd, nCmdShow);
  48.     }
  49.     BOOL UpdateWindow(void) const
  50.     {
  51.         return ::UpdateWindow(m_hWnd);
  52.     }
  53. };

  KWindow.cpp文件

  1. #define WIN32_LEAN_AND_MEAN
  2. #include <windows.h>
  3. #include <assert.h>
  4. #include <tchar.h>
  5. #include "KWindow.h"
  6. // 真正的消息分发/处理函数
  7. LRESULT KWindow::WndProc(HWND hWnd, UINT uMsg,
  8.                          WPARAM wParam, LPARAM lParam)
  9. {
  10.     switch( uMsg )
  11.     {
  12.     case WM_KEYDOWN:
  13.         OnKeyDown(wParam, lParam);
  14.         return 0;
  15.     case WM_PAINT:
  16.         {
  17.             PAINTSTRUCT ps;
  18.             BeginPaint(m_hWnd, &ps);
  19.             OnDraw(ps.hdc);
  20.             EndPaint(m_hWnd, &ps);
  21.         }
  22.         return 0;
  23.     case WM_DESTROY:
  24.         PostQuitMessage(0);
  25.         return 0;
  26.     }
  27.     return DefWindowProc(hWnd, uMsg, wParam, lParam);
  28. }
  29. // API中注册的消息处理函数,将操作系统的消息分发到正确的KWindow对象
  30. LRESULT CALLBACK KWindow::WindowProc(HWND hWnd, UINT uMsg,
  31.                                      WPARAM wParam, LPARAM lParam)
  32. {
  33.     KWindow * pWindow;
  34.     if ( uMsg == WM_NCCREATE )    // 窗口创建时收到的第一个消息
  35.     {
  36.         // 通过lParam找出该窗口对应的KWindow指针,并调用
  37.         // SetWindowLong(GWL_USERDATA)保存
  38.         assert( ! IsBadReadPtr((void *) lParam,
  39.             sizeof(CREATESTRUCT)) );
  40.         MDICREATESTRUCT * pMDIC = (MDICREATESTRUCT *)
  41.             ((LPCREATESTRUCT) lParam)->lpCreateParams;
  42.         pWindow = (KWindow *) (pMDIC->lParam);
  43.         assert( ! IsBadReadPtr(pWindow, sizeof(KWindow)) );
  44.         SetWindowLong(hWnd, GWL_USERDATA, (LONG) pWindow);
  45.     }
  46.     else
  47.     {
  48.         // 调用GetWindowLong(GWL_USERDATA)找回在WM_NCCREATE消息中保存的
  49.         // KWindow指针
  50.         pWindow=(KWindow *)GetWindowLong(hWnd, GWL_USERDATA);
  51.     }
  52.     if ( pWindow )
  53.     {
  54.         return pWindow->WndProc(hWnd, uMsg, wParam, lParam);
  55.     }
  56.     else
  57.     {
  58.         return DefWindowProc(hWnd, uMsg, wParam, lParam);
  59.     }
  60. }
  61. bool KWindow::RegisterClass(LPCTSTR lpszClass, HINSTANCE hInst)
  62. {
  63.     WNDCLASSEX wc;
  64.     if ( ! GetClassInfoEx(hInst, lpszClass, &wc) )
  65.     {
  66.         GetWndClassEx(wc);
  67.         wc.hInstance     = hInst;
  68.         wc.lpszClassName = lpszClass;
  69.         if ( !RegisterClassEx(&wc) )
  70.             return false;
  71.     }
  72.     return true;
  73. }
  74. bool KWindow::CreateEx(DWORD dwExStyle,
  75.                        LPCTSTR lpszClass, LPCTSTR lpszName, DWORD dwStyle,
  76.                        int x, int y, int nWidth, int nHeight, HWND hParent,
  77.                        HMENU hMenu, HINSTANCE hInst)
  78. {
  79.     if ( ! RegisterClass(lpszClass, hInst) )
  80.         return false;
  81.     // use MDICREATESTRUCT to pass this pointer, support MDI child window
  82.     MDICREATESTRUCT mdic;
  83.     memset(& mdic, 0, sizeof(mdic));
  84.     mdic.lParam = (LPARAM) this;
  85.     m_hWnd = CreateWindowEx(dwExStyle, lpszClass, lpszName,
  86.         dwStyle, x, y, nWidth, nHeight,
  87.         hParent, hMenu, hInst, & mdic);
  88.     return m_hWnd != NULL;
  89. }
  90. // 派生类中可以改写默认属性
  91. void KWindow::GetWndClassEx(WNDCLASSEX & wc)
  92. {
  93.     memset(& wc, 0, sizeof(wc));
  94.     wc.cbSize        = sizeof(WNDCLASSEX);
  95.     wc.style         = 0;
  96.     wc.lpfnWndProc   = WindowProc;
  97.     wc.cbClsExtra    = 0;
  98.     wc.cbWndExtra    = 0;
  99.     wc.hInstance     = NULL;
  100.     wc.hIcon         = NULL;
  101.     wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
  102.     wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
  103.     wc.lpszMenuName  = NULL;
  104.     wc.lpszClassName = NULL;
  105.     wc.hIconSm       = NULL;
  106. }
  107. // Message Loop
  108. WPARAM KWindow::MessageLoop(void)
  109. {
  110.     MSG msg;
  111.     while ( GetMessage(&msg, NULL, 0, 0) )
  112.     {
  113.         TranslateMessage(&msg);
  114.         DispatchMessage(&msg);
  115.     }
  116.     return msg.wParam;
  117. }

  如何使用它呢?请看例子:

  1. const TCHAR szHint[]    = _T("Press ESC to quit.");
  2. class KHelloWindow : public KWindow
  3. {
  4.     virtual void OnKeyDown(WPARAM wParam, LPARAM lParam)
  5.     {
  6.         if (wParam==VK_ESCAPE )
  7.         {
  8.             PostMessage(m_hWnd, WM_CLOSE, 0, 0);
  9.         }
  10.     }
  11.     virtual void OnDraw(HDC hDC)
  12.     {
  13.         TextOut(hDC, 0, 0, szHint, lstrlen(szHint));
  14.     }
  15.     // 修改默认窗口属性
  16.    virtual void GetWndClassEx(WNDCLASSEX & wc)
  17.     {
  18.         memset(& wc, 0, sizeof(wc));
  19.         wc.cbSize        = sizeof(WNDCLASSEX);
  20.         wc.style         = 0;
  21.         wc.lpfnWndProc   = WindowProc;
  22.         wc.cbClsExtra    = 0;
  23.         wc.cbWndExtra    = 0;
  24.         wc.hInstance     = NULL;
  25.         wc.hIcon         = NULL;
  26.         wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
  27.         wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
  28.         wc.lpszMenuName  = NULL;
  29.         wc.lpszClassName = NULL;
  30.         wc.hIconSm       = NULL;
  31.     }
  32. public:
  33. };
  34. int APIENTRY WinMain(HINSTANCE hInstance,
  35.                      HINSTANCE hPrevInstance,
  36.                      LPSTR     lpCmdLine,
  37.                      int       nCmdShow)
  38. {
  39.      KHelloWindow win;
  40.     win.CreateEx(0, _T("Hello"), _T("Hello"), WS_OVERLAPPEDWINDOW,GetSystemMetrics(SM_CXSCREEN)/4, GetSystemMetrics(SM_CYSCREEN)/4,
  41.         GetSystemMetrics(SM_CXSCREEN)/2,
  42.         GetSystemMetrics(SM_CYSCREEN)/2,
  43.         NULL, NULL, hInstance);
  44.     win.ShowWindow(nCmdShow);
  45.     win.UpdateWindow();
  46.     return win.MessageLoop();
  47. }

  编译运行后效果如图:

一个基于Win32的完全面向对象的窗口类

  如果大家写过稍微复杂一点的win32的程序的话,就会发现如果采用这种结构,这比一个书上典型的win32程序要简洁得多了。