你的第一Windows程序——管理应用程序状态
MSDN原文(英文)
管理应用程序状态一个窗口过程仅仅是一个为每个消息获取调用函数,所以它本质上是无状态的。因此,你需要一个方法来跟踪你的应用程序从一个函数调用下一个函数的状态。// 定义一个结构保存一些状态信息struct StateInfo { // ... (结构成员没有显示)};
StateInfo *pState = new (std::nothrow) StateInfo; if (pState == NULL) { return 0; } // 在这里初始化结构成员 (示例没有显示). HWND hwnd = CreateWindowEx( 0, // Optional window styles. CLASS_NAME, // Window class L"Learn to Program Windows", // Window text WS_OVERLAPPEDWINDOW, // Window style // Size and position CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, // Parent window NULL, // Menu hInstance, // Instance handle pState // 额外的数据信息 );
CREATESTRUCT *pCreate = reinterpret_cast<CREATESTRUCT*>(lParam);
pState = reinterpret_cast<StateInfo*>(pCreate->lpCreateParams);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pState);
LONG_PTR ptr = GetWindowLongPtr(hwnd, GWLP_USERDATA); StateInfo *pState = reinterpret_cast<StateInfo*>(ptr);
inline StateInfo* GetAppState(HWND hwnd){ LONG_PTR ptr = GetWindowLongPtr(hwnd, GWLP_USERDATA); StateInfo *pState = reinterpret_cast<StateInfo*>(ptr); return pState;}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam){ StateInfo *pState; if (uMsg == WM_CREATE) { CREATESTRUCT *pCreate = reinterpret_cast<CREATESTRUCT*>(lParam); pState = reinterpret_cast<StateInfo*>(pCreate->lpCreateParams); SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pState); } else { pState = GetAppState(hwnd); } switch (uMsg) { // Remainder of the window procedure not shown ... } return TRUE;}
// 伪代码LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam){ StateInfo *pState; /* Get pState from the HWND. */ switch (uMsg) { case WM_SIZE: HandleResize(pState, ...); break; case WM_PAINT: HandlePaint(pState, ...); break; // And so forth. }}
// 伪代码LRESULT MyWindow::WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam){ switch (uMsg) { case WM_SIZE: this->HandleResize(...); break; case WM_PAINT: this->HandlePaint(...); break; }}
template <class DERIVED_TYPE> class BaseWindow{public: static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { DERIVED_TYPE *pThis = NULL; if (uMsg == WM_NCCREATE) { CREATESTRUCT* pCreate = (CREATESTRUCT*)lParam; pThis = (DERIVED_TYPE*)pCreate->lpCreateParams; SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pThis); pThis->m_hwnd = hwnd; } else { pThis = (DERIVED_TYPE*)GetWindowLongPtr(hwnd, GWLP_USERDATA); } if (pThis) { return pThis->HandleMessage(uMsg, wParam, lParam); } else { return DefWindowProc(hwnd, uMsg, wParam, lParam); } } BaseWindow() : m_hwnd(NULL) { } BOOL Create( PCWSTR lpWindowName, DWORD dwStyle, DWORD dwExStyle = 0, int x = CW_USEDEFAULT, int y = CW_USEDEFAULT, int nWidth = CW_USEDEFAULT, int nHeight = CW_USEDEFAULT, HWND hWndParent = 0, HMENU hMenu = 0 ) { WNDCLASS wc = {0}; wc.lpfnWndProc = DERIVED_TYPE::WindowProc; wc.hInstance = GetModuleHandle(NULL); wc.lpszClassName = ClassName(); RegisterClass(&wc); m_hwnd = CreateWindowEx( dwExStyle, ClassName(), lpWindowName, dwStyle, x, y, nWidth, nHeight, hWndParent, hMenu, GetModuleHandle(NULL), this ); return (m_hwnd ? TRUE : FALSE); } HWND Window() const { return m_hwnd; }protected: virtual PCWSTR ClassName() const = 0; virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) = 0; HWND m_hwnd;};
BaseWindow是一个抽象类,从特定的窗口了派生。例如,以下是一个简单的派生类的basewindow的声明:
class MainWindow : public BaseWindow<MainWindow>{public: PCWSTR ClassName() const { return L"Sample Window Class"; } LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);};
要创建一个窗口,调用BaseWindow::Create:
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR pCmdLine, int nCmdShow){ MainWindow win; if (!win.Create(L"Learn to Program Windows", WS_OVERLAPPEDWINDOW)) { return 0; } ShowWindow(win.Window(), nCmdShow); // Run the message loop. MSG msg = { }; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return 0;}
纯虚拟的 BaseWindow::HandleMessage方法用于执行窗口过程。下面的执行是相当于开始显示窗口过程:
LRESULT MainWindow::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam){ switch (uMsg) { case WM_DESTROY: PostQuitMessage(0); return 0; case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(m_hwnd, &ps); FillRect(hdc, &ps.rcPaint, (HBRUSH) (COLOR_WINDOW+1)); EndPaint(m_hwnd, &ps); } return 0; default: return DefWindowProc(m_hwnd, uMsg, wParam, lParam); } return TRUE;}
注意,窗口句柄存储在成员变量(m_hwnd)所以我们不需要将它作为一个参数传递给HandleMessage。
许多现有的Windows编程框架,如Microsoft基础类(MFC)和活动模板库(ATL)使用的基本上类似于此处所示的方法。当然,一个完全广义的框架(如MFC)比这种相对简单的例子更复杂。