WarmGUI(1) 第一个类,用CBTHook构建CWindow (山寨版MFC::Cwnd)
转载必须注明原文转自C++博客(cppblog),作者毕达哥拉斯半圆,谢谢合作。
上一篇序言得到了很多高人的帮助鼓励和意见,并且给出了一些框架做参考,我这几天拼命消化这些信息,比较了一些架构,最终决定以完整的应用框架为主,并不先开发完整的控件库,可以先采用Windows自带的控件,或者DX控件,@vczh 的Gac控件也是可以用的嘛,@fzy 提出了快速的迭代开发方法,对我是非常好的建议,但是个人水平问题速度很可能快不起来。^_^
Any Way, 千里之行始于足下,就从最简单的窗口应用开始,请各位朋友继续批评指正以及各种吐槽~,哈哈。
Windows是以消息循环为主体,面向过程的软件结构,这是汇编、C语言对OS开发的必然结果,所以开发框架的第一步就构建面向对象的体系结构,其核心使用了CBThook,下面逐步阐述。
先回顾一个典型的Windows App是这样的:
1
int
APIENTRY _tWinMain(HINSTANCE hInstance,
2 HINSTANCE hPrevInstance,
3 LPTSTR lpCmdLine,
4 int nCmdShow)
5 {
6 UNREFERENCED_PARAMETER(hPrevInstance);
7 UNREFERENCED_PARAMETER(lpCmdLine);
8
9 MSG msg;
10 HACCEL hAccelTable;
11
12 // 初始化全局字符串
13 LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
14 LoadString(hInstance, IDC_TESTWIN32, szWindowClass, MAX_LOADSTRING);
15 //注册窗口
16 MyRegisterClass(hInstance, szWindowClass, WndProc);
17
18 // 执行应用程序初始化:
19 if ( ! InitInstance (hInstance, nCmdShow))
20 {
21 return FALSE;
22 }
23
24 while (GetMessage( & msg, NULL, 0 , 0 ))
25 {
26 if ( ! TranslateAccelerator(msg.hwnd, hAccelTable, & msg))
27 {
28 TranslateMessage( & msg);
29 DispatchMessage( & msg);
30 }
31 }
32
33 return ( int ) msg.wParam;
34 }
35
然后是很熟悉的WndProc
2 HINSTANCE hPrevInstance,
3 LPTSTR lpCmdLine,
4 int nCmdShow)
5 {
6 UNREFERENCED_PARAMETER(hPrevInstance);
7 UNREFERENCED_PARAMETER(lpCmdLine);
8
9 MSG msg;
10 HACCEL hAccelTable;
11
12 // 初始化全局字符串
13 LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
14 LoadString(hInstance, IDC_TESTWIN32, szWindowClass, MAX_LOADSTRING);
15 //注册窗口
16 MyRegisterClass(hInstance, szWindowClass, WndProc);
17
18 // 执行应用程序初始化:
19 if ( ! InitInstance (hInstance, nCmdShow))
20 {
21 return FALSE;
22 }
23
24 while (GetMessage( & msg, NULL, 0 , 0 ))
25 {
26 if ( ! TranslateAccelerator(msg.hwnd, hAccelTable, & msg))
27 {
28 TranslateMessage( & msg);
29 DispatchMessage( & msg);
30 }
31 }
32
33 return ( int ) msg.wParam;
34 }
35
1
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
2 {
3 int wmId, wmEvent;
4 PAINTSTRUCT ps;
5 HDC hdc;
6 switch (message)
7 {
8 case WM_PAINT:
9 break ;
10
11 // message
12 // message
13 // message
14
15 case WM_COMMAND:
16 break ;
17 case WM_CREATE:
18 return 0 ;
19 case WM_DESTROY:
20 PostQuitMessage( 0 );
21 break ;
22 default :
23 return DefWindowProc(hWnd, message, wParam, lParam);
24 }
25 return 0 ;
26 }
27
2 {
3 int wmId, wmEvent;
4 PAINTSTRUCT ps;
5 HDC hdc;
6 switch (message)
7 {
8 case WM_PAINT:
9 break ;
10
11 // message
12 // message
13 // message
14
15 case WM_COMMAND:
16 break ;
17 case WM_CREATE:
18 return 0 ;
19 case WM_DESTROY:
20 PostQuitMessage( 0 );
21 break ;
22 default :
23 return DefWindowProc(hWnd, message, wParam, lParam);
24 }
25 return 0 ;
26 }
27
上面的MyRegisterClass调用WinApi RegisterClassEx 注册窗口,InitInstance调用CreateWindow创建窗口,而while循环做消息处理,直到应用退出。
作为WarmGUI的第一个类CWindow,应该像MFC::CWnd那样中封装RegisterClass和CreateWindow,这应该是很平凡的事情,照做就可以了,麻烦在于对WndProc封装时,WndProc必须是一个全局的函数,只能作为CWindow的静态函数,而static修饰的函数是没有this指针的,因此在消息循环中,我们只能得到HWND句柄,却不知道这个句柄是哪一个CWindow的实例。
MFC解决的办法是使用CBT钩子,HCBT_CREATEWND类型的CBT钩子可以在窗口创建时指定回调函数,在这个回调函数中,将HWND和CWnd做一个映射,CWnd::Attach函数完成这个功能,这样WndProc就可以用FromHandlePermanent函数从hwnd找到CWnd实例,然后可以在消息处理中调用CWnd::On-Message()什么的了。
WARMGUI::CWindow将采用CBT的方法,下面是山寨过程,应该有更好的实现方法,请各位大佬多指教 ^_^
首先类CWindowManager继承std::map,定义一个HWND-CWindow map,
1
//
the CWindowManager is a map of HWND-CWindow
2 class CWindow;
3 // define the HWND-CWindow map
4 typedef map < HWND, CWindow *> CWindowMap;
5 typedef pair < HWND, CWindow *> WindowPair;
6 typedef map < HWND, CWindow *> ::iterator WndIterator;
7 typedef pair < WndIterator, bool > IterBool;
8
9 class CWindowManager : private CWindowMap
10 {
11 private :
12 CWindowManager( void );
13 ~ CWindowManager( void );
14 public :
15 bool Add(CWindow * pwnd); // add a window to map
16 bool Remove(HWND hwnd); // remove a window by hwnd
17 void Clear(); // remove all items
18 CMyWindow * Find(HWND hwnd); // find the window by hwnd
19
20 public :
21 // get CWindowManager instance as singleton pattern
22 static CWindowManager * GetInstance();
23 };
24
其中的代码是很平凡的,调用std::map的函数而已,不做举例了。
2 class CWindow;
3 // define the HWND-CWindow map
4 typedef map < HWND, CWindow *> CWindowMap;
5 typedef pair < HWND, CWindow *> WindowPair;
6 typedef map < HWND, CWindow *> ::iterator WndIterator;
7 typedef pair < WndIterator, bool > IterBool;
8
9 class CWindowManager : private CWindowMap
10 {
11 private :
12 CWindowManager( void );
13 ~ CWindowManager( void );
14 public :
15 bool Add(CWindow * pwnd); // add a window to map
16 bool Remove(HWND hwnd); // remove a window by hwnd
17 void Clear(); // remove all items
18 CMyWindow * Find(HWND hwnd); // find the window by hwnd
19
20 public :
21 // get CWindowManager instance as singleton pattern
22 static CWindowManager * GetInstance();
23 };
24
用一个全局变量
1
CWindow
*
gpInitWnd;
记住当前正在创建的CWindow*,并用互斥锁保证不会对其发生读写冲突。事实上只要保证当前线程内不发生读写冲突就可以了,因为CBT是以线程为单位创建的,不过第一个版本的CWindow暂时不考虑,以后再说。
这是CBT-hook代码:
1
static
HHOOK ghook
=
0
;
2
3 // Create a WH_CBT Hook
4 static bool HookCrate()
5 {
6 HANDLE hThread = GetCurrentThread();
7 DWORD dwThreadId = GetThreadId(hThread);
8 if (hThread) {
9 ghook = SetWindowsHookEx(
10 WH_CBT,
11 MyCBTProc, // set the CBT proc
12 0 ,
13 dwThreadId);
14 if ( ! ghook)
15 return false ;
16 }
17
18 return ( 0 );
19 }
20
21 // Destroy WH_CBT Hook
22 static void HookDestroy()
23 {
24 if (ghook) {
25 UnhookWindowsHookEx(ghook);
26 ghook = 0 ;
27 }
28 }
29
他的回调函数如下:
2
3 // Create a WH_CBT Hook
4 static bool HookCrate()
5 {
6 HANDLE hThread = GetCurrentThread();
7 DWORD dwThreadId = GetThreadId(hThread);
8 if (hThread) {
9 ghook = SetWindowsHookEx(
10 WH_CBT,
11 MyCBTProc, // set the CBT proc
12 0 ,
13 dwThreadId);
14 if ( ! ghook)
15 return false ;
16 }
17
18 return ( 0 );
19 }
20
21 // Destroy WH_CBT Hook
22 static void HookDestroy()
23 {
24 if (ghook) {
25 UnhookWindowsHookEx(ghook);
26 ghook = 0 ;
27 }
28 }
29
1
static
LRESULT CALLBACK MyCBTProc(
int
nCode, WPARAM wParam, LPARAM lParam)
2 {
3 if (nCode == HCBT_CREATEWND) {
4 // GetInitPwnd() return gpInitWnd, the window is creating
5 CWindow * pwnd = GetInitPwnd();
6 if (pwnd) {
7 HWND hwnd = pwnd ->GetSafeHwnd(); //return pwnd->_hwnd
8 if ( ! hwnd) {
9 // first time call this proc, the CWindow have no HWND yet,
10 // Attach() will attach the wParam to pwnd , eg. pwnd->_hwnd = wParam
11 pwnd -> Attach((HWND)wParam);
12 // call the PreCreateWindow before WM_CREATE
13 if ( ! (pwnd -> PreCreateWindow((LPCREATESTRUCT)lParam)))
14 return ( 1 );
15 return ( 0 );
16 } else {
17 // second time call this proc, i donw know why for now.
18 // but this is second chance to decide what is the style
19 // of the window, or the window should will be created or not,
20 // if you want create it, return 0, else return non-zero.
21 return ( 0 );
22 }
23 } else {
24 return ( 1 );
25 }
26 } else
27 return CallNextHookEx(ghook, nCode, wParam, lParam);
28 }
29
注释中我解释了每个步骤的含义,
MyCBTProc返回0则窗口被创建,返回非零则窗口被销毁。尤其要注意的是这个回调会被调用两次,我还不知道为什么,暂时也没时间搞了,请教一下高人指点。
2 {
3 if (nCode == HCBT_CREATEWND) {
4 // GetInitPwnd() return gpInitWnd, the window is creating
5 CWindow * pwnd = GetInitPwnd();
6 if (pwnd) {
7 HWND hwnd = pwnd ->GetSafeHwnd(); //return pwnd->_hwnd
8 if ( ! hwnd) {
9 // first time call this proc, the CWindow have no HWND yet,
10 // Attach() will attach the wParam to pwnd , eg. pwnd->_hwnd = wParam
11 pwnd -> Attach((HWND)wParam);
12 // call the PreCreateWindow before WM_CREATE
13 if ( ! (pwnd -> PreCreateWindow((LPCREATESTRUCT)lParam)))
14 return ( 1 );
15 return ( 0 );
16 } else {
17 // second time call this proc, i donw know why for now.
18 // but this is second chance to decide what is the style
19 // of the window, or the window should will be created or not,
20 // if you want create it, return 0, else return non-zero.
21 return ( 0 );
22 }
23 } else {
24 return ( 1 );
25 }
26 } else
27 return CallNextHookEx(ghook, nCode, wParam, lParam);
28 }
29
CWindow的Create函数是这样的:
1
bool
CWindow::Create(TCHAR
*
szClassName,
//
NULL for default class name
2 TCHAR * szWindowName,
3 DWORD dwStyle,
4 RECT & rect,
5 CWindow * pParentWnd,
6 LPVOID lpParam)
7 {
8 if ( ! pParentWnd)
9 return false ;
10
11 TCHAR * szcn = (szClassName) ? szClassName : _gszClassName;
12
13 _hInst = pParentWnd -> GetInstance();
14 if ( ! MyRegisterClass(_hInst, szcn)) {
15 DWORD dwErr = GetLastError();
16 if (dwErr != ERROR_CLASS_ALREADY_EXISTS) // 0x00000582
17 return false ;
18 }
19
20 SetInitWnd( this ); // gpInitWnd = this
21 HookCrate();
22
23 HWND hWnd = CreateWindow(szcn,
24 szWindowName,
25 dwStyle,
26 rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,
27 pParentWnd -> GetSafeHwnd(),
28 NULL, // menu
29 _hInst,
30 lpParam);
31 HookDestroy();
32 UnsetInitWnd(); // gpInitWnd = 0;
33
34 if ( ! hWnd)
35 return FALSE;
36
37 _pParent = pParentWnd;
38
39 ShowWindow(hWnd, SW_SHOW);
40 UpdateWindow(hWnd);
41
42 return TRUE;
43 }
过程如下:首先注册窗口类,然后设定gpInitWnd=自己,创建CBTHook,创建窗口,销毁CBTHook,设定gpInitWnd=0,最后显示窗口。
2 TCHAR * szWindowName,
3 DWORD dwStyle,
4 RECT & rect,
5 CWindow * pParentWnd,
6 LPVOID lpParam)
7 {
8 if ( ! pParentWnd)
9 return false ;
10
11 TCHAR * szcn = (szClassName) ? szClassName : _gszClassName;
12
13 _hInst = pParentWnd -> GetInstance();
14 if ( ! MyRegisterClass(_hInst, szcn)) {
15 DWORD dwErr = GetLastError();
16 if (dwErr != ERROR_CLASS_ALREADY_EXISTS) // 0x00000582
17 return false ;
18 }
19
20 SetInitWnd( this ); // gpInitWnd = this
21 HookCrate();
22
23 HWND hWnd = CreateWindow(szcn,
24 szWindowName,
25 dwStyle,
26 rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,
27 pParentWnd -> GetSafeHwnd(),
28 NULL, // menu
29 _hInst,
30 lpParam);
31 HookDestroy();
32 UnsetInitWnd(); // gpInitWnd = 0;
33
34 if ( ! hWnd)
35 return FALSE;
36
37 _pParent = pParentWnd;
38
39 ShowWindow(hWnd, SW_SHOW);
40 UpdateWindow(hWnd);
41
42 return TRUE;
43 }
CWindow的WndProc如下:
1
LRESULT CALLBACK CWindow::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
2 {
3 // FromHandlePermanent get CWindow* from CWindowManager(a HWND-CWindow* map)
4 // it call CWindowManager::Find(HWND hwnd) function and return CWindow*
5 // it is MFC::CWnd Function
6 CWindow * pwnd = FromHandlePermanent(hWnd);
7 if ( ! pwnd) return FirstDefaultWndProc(hWnd, message, wParam, lParam);
8
9 // Message Message and Message
10 LRESULT r = 0 ;
11 switch (message)
12 {
13 case WM_CREATE :
14 r = pwnd -> OnCreate((LPCREATESTRUCT)lParam);
15 if (r)
16 return (r);
17 break ;
18 case WM_DESTROY :
19 pwnd -> OnDestroy();
20 return ( 0 );
21 case WM_PAINT:
22 pwnd -> WindowPaint();
23 return ( 0 );
24 case WM_SIZE :
25 pwnd -> OnSize((UINT)wParam, LOWORD(lParam), HIWORD(lParam));
26 return ( 0 );
27 case WM_DISPLAYCHANGE :
28 pwnd -> WindowPaint();
29 return ( 0 );
30 // message
31 // message
32 // and message
33 default :
34 return DefWindowProc(hWnd, message, wParam, lParam);
35 }
36 return DefWindowProc(hWnd, message, wParam, lParam);
37 }
现在可以对各种消息机制做一个比较好的封装,比如对WM_PAINT调用WindowPaint函数:
2 {
3 // FromHandlePermanent get CWindow* from CWindowManager(a HWND-CWindow* map)
4 // it call CWindowManager::Find(HWND hwnd) function and return CWindow*
5 // it is MFC::CWnd Function
6 CWindow * pwnd = FromHandlePermanent(hWnd);
7 if ( ! pwnd) return FirstDefaultWndProc(hWnd, message, wParam, lParam);
8
9 // Message Message and Message
10 LRESULT r = 0 ;
11 switch (message)
12 {
13 case WM_CREATE :
14 r = pwnd -> OnCreate((LPCREATESTRUCT)lParam);
15 if (r)
16 return (r);
17 break ;
18 case WM_DESTROY :
19 pwnd -> OnDestroy();
20 return ( 0 );
21 case WM_PAINT:
22 pwnd -> WindowPaint();
23 return ( 0 );
24 case WM_SIZE :
25 pwnd -> OnSize((UINT)wParam, LOWORD(lParam), HIWORD(lParam));
26 return ( 0 );
27 case WM_DISPLAYCHANGE :
28 pwnd -> WindowPaint();
29 return ( 0 );
30 // message
31 // message
32 // and message
33 default :
34 return DefWindowProc(hWnd, message, wParam, lParam);
35 }
36 return DefWindowProc(hWnd, message, wParam, lParam);
37 }
1
void
CWindow::WindowPaint()
2 {
3 PAINTSTRUCT ps;
4 HDC hdc = BeginPaint(_hwnd, & ps);;
5
6 OnPaint(hdc, & ps);
7
8 EndPaint(_hwnd, & ps);
9 }
在这个函数内部实现BeginPaint和EndPaint对,保证我们不会因为忙乱而忘记这对冤家(常见错误之一呀),CWindow和他的子类只要重写OnPaint就可以了。
2 {
3 PAINTSTRUCT ps;
4 HDC hdc = BeginPaint(_hwnd, & ps);;
5
6 OnPaint(hdc, & ps);
7
8 EndPaint(_hwnd, & ps);
9 }
现在可以把消息循环封装进来了:
1
void
CWindow::RunMessageLoop(HACCEL hAccelTable)
2 {
3 MSG msg;
4
5 while (GetMessage( & msg, NULL, 0 , 0 ))
6 {
7 if (hAccelTable) {
8 if ( ! TranslateAccelerator(msg.hwnd, hAccelTable, & msg))
9 {
10 TranslateMessage( & msg);
11 DispatchMessage( & msg);
12 }
13 } else {
14 TranslateMessage( & msg);
15 DispatchMessage( & msg);
16 }
17 }
18 }
2 {
3 MSG msg;
4
5 while (GetMessage( & msg, NULL, 0 , 0 ))
6 {
7 if (hAccelTable) {
8 if ( ! TranslateAccelerator(msg.hwnd, hAccelTable, & msg))
9 {
10 TranslateMessage( & msg);
11 DispatchMessage( & msg);
12 }
13 } else {
14 TranslateMessage( & msg);
15 DispatchMessage( & msg);
16 }
17 }
18 }
差不多已经做完了,我们看看CWindow的声明:
1
#define msgfun virtual
2 namespace WARMGUI {
3 class WARMGUI_API CWindow
4 {
5 public :
6 CWindow( void );
7 virtual ~ CWindow( void );
8
9 HWND GetSafeHwnd();
10 HINSTANCE GetInstance();
11
12 void Attach(HWND hwnd);
13 void Dettach();
14 virtual BOOL InitInstance( HINSTANCE hInstance,
15 HINSTANCE hPrevInstance,
16 LPTSTR lpCmdLine,
17 int nCmdShow,
18 TCHAR * szClassName,
19 LPTSTR szTitle);
20
21 virtual void RunMessageLoop(HACCEL hAccelTable);
22 virtual BOOL PreCreateWindow(LPCREATESTRUCT cs);
23
24 bool Create(TCHAR * szClassName,
25 TCHAR * szWindowName,
26 DWORD dwStyle,
27 RECT & rect,
28 CWindow * pParentWnd,
29 LPVOID lpParam = 0 );
30
32 void WindowPaint();
33
34 protected :
35 HWND _hwnd;
36 HINSTANCE _hInst;
37 CWindow * _pParent;
38
39 protected :
40 /// OnCreate must return 0 to continue the creation of the CWnd object. If the application returns –1, the window will be destroyed.
41 msgfun int OnCreate (LPCREATESTRUCT cs);
42 msgfun void OnDestroy ();
43 msgfun void OnSize (UINT nType, int cx, int cy);
44 // bla bla bla hao duo message
45 // bla bla and bla hai you hao duo message
46
47 protected :
48 static CWindow * FromHandlePermanent(HWND hWnd);
49 static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
50
51 private :
52 virtual ATOM MyRegisterClass(HINSTANCE hInstance, TCHAR * szClassName);
53 };
54 }
现在可以从CWindow继承,并重载他的各种消息函数,并在WinMain中调用,一个最简单的应用框架已经有了,显然,他必须是Hello, World:
2 namespace WARMGUI {
3 class WARMGUI_API CWindow
4 {
5 public :
6 CWindow( void );
7 virtual ~ CWindow( void );
8
9 HWND GetSafeHwnd();
10 HINSTANCE GetInstance();
11
12 void Attach(HWND hwnd);
13 void Dettach();
14 virtual BOOL InitInstance( HINSTANCE hInstance,
15 HINSTANCE hPrevInstance,
16 LPTSTR lpCmdLine,
17 int nCmdShow,
18 TCHAR * szClassName,
19 LPTSTR szTitle);
20
21 virtual void RunMessageLoop(HACCEL hAccelTable);
22 virtual BOOL PreCreateWindow(LPCREATESTRUCT cs);
23
24 bool Create(TCHAR * szClassName,
25 TCHAR * szWindowName,
26 DWORD dwStyle,
27 RECT & rect,
28 CWindow * pParentWnd,
29 LPVOID lpParam = 0 );
30
32 void WindowPaint();
33
34 protected :
35 HWND _hwnd;
36 HINSTANCE _hInst;
37 CWindow * _pParent;
38
39 protected :
40 /// OnCreate must return 0 to continue the creation of the CWnd object. If the application returns –1, the window will be destroyed.
41 msgfun int OnCreate (LPCREATESTRUCT cs);
42 msgfun void OnDestroy ();
43 msgfun void OnSize (UINT nType, int cx, int cy);
44 // bla bla bla hao duo message
45 // bla bla and bla hai you hao duo message
46
47 protected :
48 static CWindow * FromHandlePermanent(HWND hWnd);
49 static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
50
51 private :
52 virtual ATOM MyRegisterClass(HINSTANCE hInstance, TCHAR * szClassName);
53 };
54 }
1
using
namespace
WARMGUI;
2
3 class CMyWindow : public CWindow {
4 public :
5 CMyWindow();
6 ~ CMyWindow();
7
8 msgfun void OnPaint(LPPAINTSTRUCE ps);x
9 };
10
11 void CMyWindow::OnPaint(HDC hdc, LPPAINTSTRUCT /* ps */ )
12 {
13 HBRUSH brush = CreateSolidBrush(RGB( 0 , 0 , 255 ));
14
15 TCHAR hello[] = L " Hello, World! " ;
16 RECT rect;
17 rect.left = 100 , rect.top = 100 , rect.right = 200 , rect.bottom = 200 ;
18 COLORREF oldBkg = SetBkColor(hdc, RGB( 255 , 255 , 0 ));
19 COLORREF oldClr = SetTextColor(hdc, RGB( 0 , 0 , 255 ));
20 DrawText(hdc, hello, _tcslen(hello), & rect, DT_CENTER);
21 SetTextColor(hdc, oldClr);
22 SetBkColor(hdc, oldBkg);
23 DeleteObject(brush);
24 }
25
26 int APIENTRY _tWinMain(HINSTANCE hInstance,
27 HINSTANCE hPrevInstance,
28 LPTSTR lpCmdLine,
29 int nCmdShow)
30 {
31 CMyWindow mainwnd;
32
33 if (mainwnd.InitInstance(hInstance, hPrevInstance, lpCmdLine, nCmdShow, szClassName, szTitle)) {
34 mainwnd.RunMessageLoop( 0 );
35 } else {
36 return ( - 1 );
37 }
38
39 return ( 0 );
40 }
41
2
3 class CMyWindow : public CWindow {
4 public :
5 CMyWindow();
6 ~ CMyWindow();
7
8 msgfun void OnPaint(LPPAINTSTRUCE ps);x
9 };
10
11 void CMyWindow::OnPaint(HDC hdc, LPPAINTSTRUCT /* ps */ )
12 {
13 HBRUSH brush = CreateSolidBrush(RGB( 0 , 0 , 255 ));
14
15 TCHAR hello[] = L " Hello, World! " ;
16 RECT rect;
17 rect.left = 100 , rect.top = 100 , rect.right = 200 , rect.bottom = 200 ;
18 COLORREF oldBkg = SetBkColor(hdc, RGB( 255 , 255 , 0 ));
19 COLORREF oldClr = SetTextColor(hdc, RGB( 0 , 0 , 255 ));
20 DrawText(hdc, hello, _tcslen(hello), & rect, DT_CENTER);
21 SetTextColor(hdc, oldClr);
22 SetBkColor(hdc, oldBkg);
23 DeleteObject(brush);
24 }
25
26 int APIENTRY _tWinMain(HINSTANCE hInstance,
27 HINSTANCE hPrevInstance,
28 LPTSTR lpCmdLine,
29 int nCmdShow)
30 {
31 CMyWindow mainwnd;
32
33 if (mainwnd.InitInstance(hInstance, hPrevInstance, lpCmdLine, nCmdShow, szClassName, szTitle)) {
34 mainwnd.RunMessageLoop( 0 );
35 } else {
36 return ( - 1 );
37 }
38
39 return ( 0 );
40 }
41