下面就是使用了ZWindow类的更新程序。
程序67.
#include <windows.h>
class ZWindow { public: HWND m_hWnd;
ZWindow(HWND hWnd = 0) : m_hWnd(hWnd) { }
inline void Attach(HWND hWnd) { m_hWnd = hWnd; }
inline BOOL ShowWindow(int nCmdShow) { return ::ShowWindow(m_hWnd, nCmdShow); }
inline BOOL UpdateWindow() { return ::UpdateWindow(m_hWnd); }
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_DESTROY: PostQuitMessage(0); break; } return ::DefWindowProc(hWnd, uMsg, wParam, lParam); } };
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { char szAppName[] = "Hello world"; HWND hWnd; MSG msg; WNDCLASS wnd; ZWindow zwnd;
wnd.cbClsExtra = NULL; wnd.cbWndExtra = NULL; wnd.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wnd.hCursor = LoadCursor(NULL, IDC_ARROW); wnd.hIcon = LoadIcon(NULL, IDI_APPLICATION); wnd.hInstance = hInstance; wnd.lpfnWndProc = ZWindow::WndProc; wnd.lpszClassName = szAppName; wnd.lpszMenuName = NULL; wnd.style = CS_HREDRAW | CS_VREDRAW;
if (!RegisterClass(&wnd)) { MessageBox(NULL, "Can not register window class", "Error", MB_OK | MB_ICONINFORMATION); return -1; }
hWnd = CreateWindow(szAppName, "Hello world", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
zwnd.Attach(hWnd);
zwnd.ShowWindow(nCmdShow); zwnd.UpdateWindow();
while (GetMessage(&msg, NULL, 0, 0)) { DispatchMessage(&msg); }
return msg.wParam; } |
这个程序只是简单示范了一下ZWindow的用法,说实话,这个类就不会做什么特别的了。它只是对Windows API的一层包装,唯一的优点就是你不需要传递HWND参数了,但是你必须得在调用成员函数的时候输入对象的名称。
对于以前,你这样调用函数: ShowWindow(hWnd, nCmdShow);
现在,你可以这么做: zwnd.ShowWindow(nCmdShow);
到现在为止,这并不是一个明显的优点。
我们来看看在WndProc中如何处理窗口消息。在前一个程序中,我们只处理了一个函数,也就是WM_DESTROY。如果你想要处理更多的消息,那么可以在switch语句中加入更多的case。让我们来修改一下WndProc,处理一下WM_PAINT。就像这个样子:
switch (uMsg) { case WM_PAINT: hDC = ::BeginPaint(hWnd, &ps); ::GetClientRect(hWnd, &rect); ::DrawText(hDC, "Hello world", -1, &rect, DT_CENTER | DT_VCENTER DT_SINGLELINE); ::EndPaint(hWnd, &ps); break; case WM_DESTROY: ::PostQuitMessage(0); break; } |
这个代码很正确,它会在窗口的正中显示Hello world。但是,为什么要用BeginPaint、GetClientRect和EndPaint这些API呢?根据我们的标准,这些API都应该作为ZWindow的成员函数来使用的——它们的第一个参数都是HWND。
因为所有这些函数都是非static函数。并且,你不能在static成员函数中调用非static成员函数。为什么呢?因为它们的区别就是this指针,非static成员函数拥有this指针,而static函数没有。如果我们通过某种手段将this指针传递给了static成员函数,那么我们就可以在static成员函数中调用非static成员函数了。让我们看看下面的程序。
程序68.
#include <iostream> using namespace std;
class C { public: void NonStaticFunc() { cout << "NonStaticFun" << endl; }
static void StaticFun(C* pC) { cout << "StaticFun" << endl; pC->NonStaticFunc(); } };
int main() { C objC; C::StaticFun(&objC); return 0; } |
程序的输出为:
所以,我们就可以使用和这里相同的技术,也就是将ZWindow对象的地址存入一个全局变量,然后利用这个指针调用非static成员函数。下面是前一个程序的更新版本,在其中我们没有直接调用窗口的API。
程序69.
#include <windows.h>
class ZWindow;
ZWindow* g_pWnd = NULL;
class ZWindow { public: HWND m_hWnd;
ZWindow(HWND hWnd = 0) : m_hWnd(hWnd) { }
inline void Attach(HWND hWnd) { m_hWnd = hWnd; }
inline BOOL ShowWindow(int nCmdShow) { return ::ShowWindow(m_hWnd, nCmdShow); }
inline BOOL UpdateWindow() { return ::UpdateWindow(m_hWnd); }
inline HDC BeginPaint(LPPAINTSTRUCT ps) { return ::BeginPaint(m_hWnd, ps); }
inline BOOL EndPaint(LPPAINTSTRUCT ps) { return ::EndPaint(m_hWnd, ps); }
inline BOOL GetClientRect(LPRECT rect) { return ::GetClientRect(m_hWnd, rect); }
BOOL Create(LPCTSTR szClassName, LPCTSTR szTitle, HINSTANCE hInstance, HWND hWndParent = 0, DWORD dwStyle = WS_OVERLAPPEDWINDOW, DWORD dwExStyle = 0, HMENU hMenu = 0) { m_hWnd = ::CreateWindowEx(dwExStyle, szClassName, szTitle, dwStyle, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, hWndParent, hMenu, hInstance, NULL);
return m_hWnd != NULL; }
static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { ZWindow* pThis = g_pWnd; HDC hDC; PAINTSTRUCT ps; RECT rect;
switch (uMsg) { case WM_PAINT: hDC = pThis->BeginPaint(&ps); pThis->GetClientRect(&rect); ::DrawText(hDC, "Hello world", -1, &rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE); pThis->EndPaint(&ps); break;
case WM_DESTROY: ::PostQuitMessage(0); break; }
return ::DefWindowProc(hWnd, uMsg, wParam, lParam); } };
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { char szAppName[] = "Hello world"; MSG msg; WNDCLASS wnd; ZWindow zwnd;
wnd.cbClsExtra = NULL; wnd.cbWndExtra = NULL; wnd.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wnd.hCursor = LoadCursor(NULL, IDC_ARROW); wnd.hIcon = LoadIcon(NULL, IDI_APPLICATION); wnd.hInstance = hInstance; wnd.lpfnWndProc = zwnd.WndProc; wnd.lpszClassName = szAppName; wnd.lpszMenuName = NULL; wnd.style = CS_HREDRAW | CS_VREDRAW;
if (!RegisterClass(&wnd)) { MessageBox(NULL, "Can not register window class", "Error", MB_OK | MB_ICONINFORMATION); return -1; }
g_pWnd = &zwnd; zwnd.Create(szAppName, "Hell world", hInstance); zwnd.ShowWindow(nCmdShow); zwnd.UpdateWindow();
while (GetMessage(&msg, NULL, 0, 0)) { DispatchMessage(&msg); }
return msg.wParam; } |
那么,我们终于有了这个可以工作的程序。现在,让我们来利用面向对象程序设计。如果我们对于每个消息都调用函数,并且使这些函数都成为虚函数的话,那么我们就可以在继承ZWindow类之后调用这些函数了。所以,我们可以自定义ZWindow的默认行为。现在,WndProc是类似这个样子:
static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { ZWindow* pThis = g_pWnd;
switch (uMsg) { case WM_CREATE: pThis->OnCreate(wParam, lParam); break;
case WM_PAINT: pThis->OnPaint(wParam, lParam); break;
case WM_DESTROY: ::PostQuitMessage(0); break; } return ::DefWindowProc(hWnd, uMsg, wParam, lParam); } |
在这里,OnCreate和OnPaint是虚函数。并且,当我们从ZWindow继承一个类的时候,我们就可以重写所有我们想自定义的这些函数。
|