The new scratch program - The Old New Thing (microsoft.com)https://devblogs.microsoft.com/oldnewthing/20050422-08/?p=35813
Raymond Chen 2005年4月22日
译注:此篇是 翻译《The Old New Thing》 - The scratch program 姊妹篇,对 scratch 程序作作了升级,采用了现代 C++ 编程风格,避免了全局状态,并通过虚函数和基类指针提供了扩展性。
我认为是时候更新我们过去一年来一直在使用的 scratch 程序了。我听说有一种叫做 C++ 的新语言,它很快就会变得非常流行,所以让我们也来跟风吧!
#define STRICT
#define UNICODE
#define _UNICODE
#include <windows.h>
#include <windowsx.h>
#include <ole2.h>
#include <commctrl.h>
#include <shlwapi.h>
#include <shlobj.h>
#include <shellapi.h>
HINSTANCE g_hinst;
class Window
{
public:
HWND GetHWND() { return m_hwnd; }
protected:
virtual LRESULT HandleMessage(
UINT uMsg, WPARAM wParam, LPARAM lParam);
virtual void PaintContent(PAINTSTRUCT *pps) { }
virtual LPCTSTR ClassName() = 0;
virtual BOOL WinRegisterClass(WNDCLASS *pwc)
{ return RegisterClass(pwc); }
virtual ~Window() { }
HWND WinCreateWindow(DWORD dwExStyle, LPCTSTR pszName,
DWORD dwStyle, int x, int y, int cx, int cy,
HWND hwndParent, HMENU hmenu)
{
Register();
return CreateWindowEx(dwExStyle, ClassName(), pszName, dwStyle,
x, y, cx, cy, hwndParent, hmenu, g_hinst, this);
}
private:
void Register();
void OnPaint();
void OnPrintClient(HDC hdc);
static LRESULT CALLBACK s_WndProc(HWND hwnd,
UINT uMsg, WPARAM wParam, LPARAM lParam);
protected:
HWND m_hwnd;
};
void Window::Register()
{
WNDCLASS wc;
wc.style = 0;
wc.lpfnWndProc = Window::s_WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = g_hinst;
wc.hIcon = NULL;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszMenuName = NULL;
wc.lpszClassName = ClassName();
WinRegisterClass(&wc);
}
LRESULT CALLBACK Window::s_WndProc(
HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
Window *self;
if (uMsg == WM_NCCREATE) {
LPCREATESTRUCT lpcs = reinterpret_cast<LPCREATESTRUCT>(lParam);
self = reinterpret_cast<Window *>(lpcs->lpCreateParams);
self->m_hwnd = hwnd;
SetWindowLongPtr(hwnd, GWLP_USERDATA,
reinterpret_cast<LPARAM>(self));
} else {
self = reinterpret_cast<Window *>
(GetWindowLongPtr(hwnd, GWLP_USERDATA));
}
if (self) {
return self->HandleMessage(uMsg, wParam, lParam);
} else {
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
}
LRESULT Window::HandleMessage(
UINT uMsg, WPARAM wParam, LPARAM lParam)
{
LRESULT lres;
switch (uMsg) {
case WM_NCDESTROY:
lres = DefWindowProc(m_hwnd, uMsg, wParam, lParam);
SetWindowLongPtr(m_hwnd, GWLP_USERDATA, 0);
delete this;
return lres;
case WM_PAINT:
OnPaint();
return 0;
case WM_PRINTCLIENT:
OnPrintClient(reinterpret_cast<HDC>(wParam));
return 0;
}
return DefWindowProc(m_hwnd, uMsg, wParam, lParam);
}
void Window::OnPaint()
{
PAINTSTRUCT ps;
BeginPaint(m_hwnd, &ps);
PaintContent(&ps);
EndPaint(m_hwnd, &ps);
}
void Window::OnPrintClient(HDC hdc)
{
PAINTSTRUCT ps;
ps.hdc = hdc;
GetClientRect(m_hwnd, &ps.rcPaint);
PaintContent(&ps);
}
class RootWindow : public Window
{
public:
virtual LPCTSTR ClassName() { return TEXT("Scratch"); }
static RootWindow *Create();
protected:
LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
LRESULT OnCreate();
private:
HWND m_hwndChild;
};
LRESULT RootWindow::OnCreate()
{
return 0;
}
LRESULT RootWindow::HandleMessage(
UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg) {
case WM_CREATE:
return OnCreate();
case WM_NCDESTROY:
// Death of the root window ends the thread
PostQuitMessage(0);
break;
case WM_SIZE:
if (m_hwndChild) {
SetWindowPos(m_hwndChild, NULL, 0, 0,
GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam),
SWP_NOZORDER | SWP_NOACTIVATE);
}
return 0;
case WM_SETFOCUS:
if (m_hwndChild) {
SetFocus(m_hwndChild);
}
return 0;
}
return __super::HandleMessage(uMsg, wParam, lParam);
}
RootWindow *RootWindow::Create()
{
RootWindow *self = new RootWindow();
if (self && self->WinCreateWindow(0,
TEXT("Scratch"), WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL)) {
return self;
}
delete self;
return NULL;
}
int PASCAL
WinMain(HINSTANCE hinst, HINSTANCE, LPSTR, int nShowCmd)
{
g_hinst = hinst;
if (SUCCEEDED(CoInitialize(NULL))) {
InitCommonControls();
RootWindow *prw = RootWindow::Create();
if (prw) {
ShowWindow(prw->GetHWND(), nShowCmd);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
CoUninitialize();
}
return 0;
}
这个程序的基本思想与我们旧的临时程序相同,但现在它有了新鲜的柠檬味 C++ 气息。我们不是在全局变量中保存状态,而是声明了一个 C++ 类并将其与窗口关联起来。
为了简单起见,对象的生命周期与窗口本身绑定在一起。
首先,有一个基本的 Window
类,我们将把它作为我们未来任何“与窗口关联的类”工作的基类。
目前唯一的派生类是 RootWindow
,这是顶级框架窗口,目前是程序使用的唯一窗口。
你可能已经猜到,随着需求的出现,我们以后可能会有其他派生类。
WinRegisterClass
方法之所以是虚拟的(并且没有做任何有趣的事情),是为了让派生类可以修改注册类时使用的 WNDCLASS
。
我目前没有立即需要它,但如果我需要,拿来就能用。
我们使用 GWLP_USERDATA
窗口长指针来存储关联类的指针,从而允许我们从窗口句柄中恢复对象。
请注意,在 RootWindow::HandleMessage
方法中,我使用了 Visual C++ 的 __super
扩展。如果你不想依赖非标准扩展,你可以改为编写:
class RootWindow : public Window {
public:
typedef Window super;
...(其余代码)
}
并使用 super
代替 __super
。
这个程序本身没有做什么有趣的事情;它只是未来示例的一个框架。