browser icon
You are using an insecure version of your web browser. Please update your browser!
Using an outdated browser makes your computer unsafe. For a safer, faster, more enjoyable user experience, please update your browser today or try a newer browser.

ATL的GUI程序设计(2)

Posted by on 2006 年 01 月 24 日

你可以任意转载本文,但请在转载后的文章中注明作者和原始链接。
媒体约稿请联系 titilima_AT_163.com(把“_AT_”换成“@”)。

第二章 一个最简单窗口程序的转型

我知道,可能会有很多朋友对上一章的“Hello, World!”ATL版不以为然,因为它并不能算是什么ATL程序——毕竟它只不过是有了个CComModule而已。不过不管怎样我还是要说,它几乎仍然拥有了一个ATL GUI程序的所有组成部分:入口、初始化、程序体、卸载……
“等等!”也许你会突然打断我,“——还有注册窗口类、消息循环呢?”
当然,对于一个完整的GUI程序来讲,这也是必要的。

貌似废话

不清楚你是否已经为本章的内容做好了准备,因为下面我们就要动真格的了。不过考虑到本书的读者群中可能会存在着相当一部分了解MFC却对Win32 GUI的基本原理和流程不甚熟悉的朋友,所以李马特别为你们准备了这一节的内容。SDK的粉丝们可以跳过这一节,如果你们觉得李马讲的有些拖沓冗长的话。
那么,我还是先以一个标准的Win32 SDK程序开始:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
//////////////////////////////////////////////////////////////////////////   
// ATL的GUI程序设计配套源代码   
// 第二章 一个最简单窗口程序的转型   
// 工程名称:HelloSDK   
// 作者:李马   
// http://www.titilima.cn   
//////////////////////////////////////////////////////////////////////////   
 
#include <windows.h>   
#include <tchar.h>   
 
LRESULT CALLBACK HelloWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )   
{   
    switch ( uMsg )   
    {   
    case WM_DESTROY:   
        {   
            PostQuitMessage( 0 );   
        }   
        break;   
    case WM_PAINT:   
        {   
            HDC hdc;   
            PAINTSTRUCT ps;   
 
            hdc = BeginPaint( hWnd, &ps );   
            DrawText( hdc, _T("Hello, SDK!"), -1, &ps.rcPaint, DT_CENTER | DT_VCENTER | DT_SINGLELINE );   
            EndPaint( hWnd, &ps );   
        }   
        break;   
    default:   
        return DefWindowProc( hWnd, uMsg, wParam, lParam );   
    }   
    return 0;   
}   
 
BOOL InitApplication( HINSTANCE hInstance )   
{   
    WNDCLASS wc;   
    wc.cbClsExtra    = 0;   
    wc.cbWndExtra    = 0;   
    wc.hbrBackground = (HBRUSH)GetStockObject( WHITE_BRUSH );   
    wc.hCursor       = LoadCursor( NULL, IDC_ARROW );   
    wc.hIcon         = LoadIcon( NULL, IDI_APPLICATION );   
    wc.hInstance     = hInstance;   
    wc.lpfnWndProc   = HelloWndProc;   
    wc.lpszClassName = _T("HelloSDK");   
    wc.lpszMenuName  = NULL;   
    wc.style         = CS_HREDRAW | CS_VREDRAW;   
 
    return RegisterClass( &wc );   
}   
 
int WINAPI _tWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nShowCmd )   
{   
    // 注册窗口类   
    InitApplication( hInstance );   
 
    // 创建窗口   
    HWND hWnd = CreateWindow( _T("HelloSDK"), _T("Hello SDK"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,   
        CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL );   
    ShowWindow( hWnd, nShowCmd );   
    UpdateWindow( hWnd );   
 
    // 消息循环   
    MSG msg;   
    while ( GetMessage( &msg, NULL, 0, 0 ) )   
    {   
        TranslateMessage( &msg );   
        DispatchMessage( &msg );   
    }   
 
    return msg.wParam;   
}

不知道你是否会觉得这段代码有些冗长?事实上,这个程序已经体现了Win32 GUI程序运行的所有流程(请注意,我并不会对这些代码进行详细的解释,因为我已经假设你已经了解了这些代码具体行为的必要细节。如果不是这样的话,请参考相关的书籍或者MSDN):

  1. 注册窗口类的部分。在这个程序中,InitApplication函数完成了这一工作。窗口类的概念类似于OO(面向对象)中的类,所有你在Windows中能看到的窗口都是某个特定窗口类的一份实例。但是,窗口类并非任何一种OOP语言中的类——它所包括的并不是通称的属性和方法(在C++中称作成员变量和成员函数),而是属性和响应。这个区别可能会使你感到费解,我会在下一章中为你详细介绍——因为ATL中对窗口的封装类将这一点体现得十分淋漓尽致。
  2. 创建窗口的部分。在通常的SDK代码里,这些代码被封装在一个名为InitInstance的函数中。这段代码所做的工作一般是创建窗口并将其显示出来。
  3. 消息循环。Windows是一个基于消息机制的操作系统,各个窗口之间的通信也主要是靠Windows消息来完成的。而程序中的消息循环也就是将本程序UI线程中的消息队列中提取各种消息,进行处理(如果有必要的话)之后分发给各个消息的属主窗口(或者说是目标窗口)。

在这里需要指出的是,HelloWndProc是我们自己定义的一个函数,我们需要用它来控制我们对特定窗口消息的特定响应。我们只需要在注册窗口类之前,将这个函数的地址(也就是函数名)赋值给WNDCLASS::lpfnWndProc成员就可以了。这个函数我们自己不需要进行调用,它的调用是当我们的窗口收到窗口消息后,由Windows完成的。在这个回调函数中,我们的处理是这样的:

  • WM_DESTROY。在窗口被销毁的时候,窗口会收到此消息。在这里,我们会调用PostQuitMessage,用以向当前UI线程的消息队列之中发送一条WM_QUIT消息,GetMessage在收到这条消息后,会返回FALSE,也就结束了消息循环,WinMain也就结束了。
  • WM_PAINT。在窗口需要绘制的时候,窗口会收到此消息。在这里我们只是简单的在窗口的中间绘制了一行文字“Hello, SDK!”。
  • 其它消息。这些消息都是我们不关心的,所以我们将其交由系统默认的窗口过程DefWindowProc来处理。

这段代码貌似冗长,但实际上还是很有条理的,你可以根据它以及我以上的解说来对照这个程序的ATL版本。

ATL等同品

在写作这本书的时候,我总是希望我每次都能够能使用让你不太陌生的代码来循序渐进地引导你。考虑再三,对于“Hello, ATL!”的这个程序,我决定先把它的WinMain展现给你:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int WINAPI _tWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nShowCmd )   
{   
    _Module.Init( NULL, hInstance );   
 
    // 创建窗口   
    CHelloATLWnd wnd;   
    wnd.Create( NULL, CHelloATLWnd::rcDefault, _T("Hello ATL") );   
    wnd.ShowWindow( nShowCmd );   
    wnd.UpdateWindow();   
 
    // 消息循环   
    MSG msg;   
    while ( GetMessage( &msg, NULL, 0, 0 ) )   
    {   
        TranslateMessage( &msg );   
        DispatchMessage( &msg );   
    }   
 
    _Module.Term();   
    return msg.wParam;   
}

OK,上一章介绍过的_Module又出现在你的眼前了——不过还是没有什么特别的变化,仍然是那熟悉的Init和Term。而且,正如“山哟还是那座山”一样,消息循环哟也仍然是那个消息循环。当然,你肯定也发现了那寥寥的变化:CHelloATLWnd是什么?在我将它的代码展现给你之前,你可能会做出这样的猜想:

  • 这是一个C++类,它对Win32窗口类进行了封装。
  • 这个类封装了大多数窗口操作的API函数,诸如CreateWindow、ShowWindow、UpdateWindow。
  • 窗口类的注册可能也是在这个C++类中完成的。

好,打住,这就够了。让我们来撩开CHelloATLWnd那貌似神秘的面纱吧,赶紧着。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class CHelloATLWnd : public CWindowImpl< CHelloATLWnd, CWindow, CWinTraits< WS_OVERLAPPEDWINDOW > >   
{   
public:   
    CHelloATLWnd()   
    {   
        CWndClassInfo& wci     = GetWndClassInfo();   
        wci.m_bSystemCursor    = TRUE;   
        wci.m_lpszCursorID     = IDC_ARROW;   
        wci.m_wc.hbrBackground = (HBRUSH)GetStockObject( WHITE_BRUSH );   
        wci.m_wc.hIcon         = LoadIcon( NULL, IDI_APPLICATION );   
    }   
public:   
    DECLARE_WND_CLASS( _T("HelloATL") )   
public:   
    BEGIN_MSG_MAP( CHelloATLWnd )   
        MESSAGE_HANDLER( WM_DESTROY, OnDestroy )   
        MESSAGE_HANDLER( WM_PAINT, OnPaint )   
    END_MSG_MAP()   
public:   
    LRESULT OnDestroy( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& hHandled )   
    {   
        ::PostQuitMessage( 0 );   
        return 0;   
    }   
    LRESULT OnPaint( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& hHandled )   
    {   
        HDC hdc;   
        PAINTSTRUCT ps;   
 
        hdc = BeginPaint( &ps );   
        DrawText( hdc, _T("Hello, ATL!"), -1, &ps.rcPaint, DT_CENTER | DT_VCENTER | DT_SINGLELINE );   
        EndPaint( &ps );   
        return 0;   
    }   
};

猜想,还是猜想!

请允许我在本章中不为你解释这个类的任何具体细节,取而代之的是继续的猜想。因为,这个类中需要解释的东西太多了,以至于我必须为它单独开辟一章。

  • 窗口类的注册是由这个C++类的构造函数与DECLARE_WND_CLASS宏一起完成的。
  • 对于BEGIN_MSG_MAP与END_MSG_MAP这一部分,想必使用过MFC的朋友们应该更容易理解。是的,这一对宏可以算作ATL的消息映射,在其中由MESSAGE_HANDLER作为消息分流器,将各种窗口消息分配给各个处理函数。
  • 创建窗口时指定的样式貌似和模板参数CWinTraits有关。

当然,除了这些猜想之外,你可能还会同时存在以下疑问:

  • CWindowImpl、CWindow、CWinTraits究竟是什么?
  • 窗口类是在何时注册的?
  • 消息分流器是如何实现的?

也许你还会有更多的疑问,那么就让我一并将它们留到下一章再解决吧。如果你实在等不及的话,atlwin.h的代码也会告诉你一切的。

补叙CComModule

由于这本书主要针对的是ATL 3.0/Visual C++ 6.0,所以我疏忽了对CComModule的研究。在此感谢老李老刀兄提出的一点,就是CComModule在ATL 7.0中已经不建议使用了。于是我将MSDN中的相关章节摘抄下来,权作借花献佛之用。

不过,出于代码的兼容性以及WTL的内容考虑,本系列后续文章仍然将使用ATL 3.0中的CComModule。

CComModule 替换类

ATL 的早期版本使用 CComModule。在 ATL 7.0 中,CComModule 功能被若干个类所取代:

  • CAtlBaseModule 包含大多数使用 ATL 的应用程序所需的信息。包含模块和资源实例的 HINSTANCE。
  • CAtlComModule 包含 ATL 中的 COM 类所需的信息。
  • CAtlWinModule 包含 ATL 中的窗口化类所需的信息。
  • CAtlDebugInterfacesModule 包含接口调试支持。
  • CAtlModule 下列 CAtlModule 派生的类被自定义为包含特定应用程序类型中所需的信息。这些类中的大部分成员都可以被重写:
    CAtlDllModuleT 在 DLL 应用程序中使用。为标准导出提供代码。
    CAtlExeModuleT 在 EXE 应用程序中使用。提供 EXE 中所需的代码。
    CAtlServiceModuleT 为创建 Windows NT 和 Windows 2000 服务提供支持。
    CComModule 仍然可用以便向后兼容。

分布 CComModule 功能的原因

由于以下原因,CComModule 的功能分布到了几个新类中:

  • 使 CComModule 中的功能呈粒状分割。
    对 COM、窗口化、接口调试和应用程序特定的(DLL 或 EXE)功能的支持现在在不同的类中。
  • 自动为这些模块的每一个声明全局实例。
    所需模块类的全局实例链接到项目中。
  • 消除了调用 Init 和 Term 方法的必要性。
    Init 和 Term 方法已移动到模块类的构造函数和析构函数中;不再需要调用 Init 和 Term。

附件:atlgui02.zip

订阅本站

8 Comments

  • At 2006.01.24 23:02, adorp said:

    “我知道,可能会有很多朋友对上一章的“Hello, World!”ATL版不以为然”, 可俺 adorp 深以为然:)
    今朝, 终于做了沙发了, 马哥, 新春快乐呵!

    • At 2006.01.24 23:10, adorp said:

      小马哥, 把您的email 地址发到俺的email里吧:
      jerysun0818#yahoo.com.cn

      (1. 俺不用QQ只用”伊妹儿, 以后考虑可用MSN:)
      (2. 注意把#换成@哦, 哎, spam忒多了:)
      (3. 很想向您学WinCE的开发)
      (4. 寒假就天天盼您发表新的章节啦)
      (5. 你需要的话, 收齐了, 我可以给您做成chm文件)

      • At 2006.01.25 09:10, 李马 said:

        我的email:titilima@163.com。
        关于chm的问题,我可能的确没有时间制作,所以大家如果觉得这本书值得制作的话,请制作完了发给我一份就可以了。

        • At 2006.01.30 21:01, 诚心请教 said:

          #include

          class ZWindow;

          ZWindow* g_pWnd = NULL;

          #pragma pack(push,1)
          struct _WndProcThunk
          {
          DWORD m_mov; // mov dword ptr [esp+0x4], pThis (esp+0x4 is hWnd)
          DWORD m_this;
          BYTE m_jmp; // jmp WndProc
          DWORD m_relproc; // 相对jmp
          };
          #pragma pack(pop)

          typedef LRESULT (CALLBACK* WNDPROC)(HWND, UINT, WPARAM, LPARAM);

          class ZWindow
          {
          public:
          HWND m_hWnd;

          _WndProcThunk thunk;

          ZWindow(HWND hWnd = 0) : m_hWnd(hWnd) { }

          void Init(WNDPROC proc, void* pThis)
          {
          thunk.m_mov = 0x042444C7; //C7 44 24 04
          thunk.m_this = (DWORD)pThis;
          thunk.m_jmp = 0xe9;
          thunk.m_relproc = (int)proc – ((int)this+sizeof(_WndProcThunk));

          FlushInstructionCache(GetCurrentProcess(), &thunk, sizeof(thunk));
          }

          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, int x = CW_USEDEFAULT,
          int y = CW_USEDEFAULT, int nWidth = CW_USEDEFAULT,
          int nHeight = CW_USEDEFAULT)
          {
          m_hWnd = ::CreateWindowEx(dwExStyle, szClassName, szTitle, dwStyle,
          x, y, nWidth, nHeight,
          hWndParent, hMenu, hInstance, NULL);

          return m_hWnd != NULL;

          }

          virtual LRESULT OnPaint(WPARAM wParam, LPARAM lParam)
          {
          HDC hDC;
          PAINTSTRUCT ps;
          RECT rect;

          hDC = BeginPaint(&ps);
          GetClientRect(&rect);
          ::DrawText(hDC, “Hello world”, -1, &rect,
          DT_CENTER | DT_VCENTER | DT_SINGLELINE);
          EndPaint(&ps);
          return 0;
          }

          virtual LRESULT OnLButtonDown(WPARAM wParam, LPARAM lParam)
          {
          return 0;
          }

          virtual LRESULT OnCreate(WPARAM wParam, LPARAM lParam)
          {
          return 0;
          }

          virtual LRESULT OnKeyDown(WPARAM wParam, LPARAM lParam)
          {
          return 0;
          }

          static LRESULT CALLBACK StartWndProc(HWND hWnd, UINT uMsg,
          WPARAM wParam, LPARAM lParam)
          {
          ZWindow* pThis = g_pWnd ;
          pThis->m_hWnd = hWnd;
          pThis->Init(WindowProc, pThis);
          WNDPROC pProc = (WNDPROC)&(pThis->thunk);
          WNDPROC pOldProc = (WNDPROC)::SetWindowLong(hWnd, GWL_WNDPROC, (LONG)pProc)
          pProc(hWnd, uMsg, wParam, lParam);
          return 0;
          }

          static LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg,
          WPARAM wParam, LPARAM lParam)
          {

          ZWindow* pThis = (ZWindow*)hWnd;
          pThis->ProcessWindowMessage(pThis->m_hWnd, uMsg, wParam, lParam) ;
          return 0;
          }

          BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg,
          WPARAM wParam, LPARAM lParam)
          {
          if (uMsg == WM_NCDESTROY)
          ::PostQuitMessage(0);

          switch (uMsg)
          {
          case WM_CREATE:
          OnCreate(wParam, lParam);
          break;

          case WM_PAINT:
          OnPaint(wParam, lParam);
          break;

          case WM_LBUTTONDOWN:
          OnLButtonDown(wParam, lParam);
          break;

          case WM_KEYDOWN:
          OnKeyDown(wParam, lParam);
          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 zwnd1;

          wnd.cbClsExtra = NULL;
          wnd.cbWndExtra = NULL;
          wnd.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH);
          wnd.hCursor = LoadCursor(NULL, IDC_ARROW);
          wnd.hIcon = LoadIcon(NULL, IDI_APPLICATION);
          wnd.hInstance = hInstance;
          wnd.lpfnWndProc = ZWindow::StartWndProc;
          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 = &zwnd1;
          zwnd1.Create(szAppName, “Hell world”, hInstance);

          zwnd1.ShowWindow(nCmdShow);
          zwnd1.UpdateWindow();

          while (GetMessage(&msg, NULL, 0, 0))
          {
          DispatchMessage(&msg);
          }

          return msg.wParam;

          }

          我刚学atl的gui,不明白上面程序为什么错?
          诚心请教。。

          • At 2006.01.31 10:34, 李马 said:

            to 诚心请教:
            你这里主要存在两个问题。
            thunk.m_relproc = (int)proc – ((int)this+sizeof(_WndProcThunk));
            首先是这一句,《ATL under the hood》原文中是将thunk进行了封装,而你是直接将这个结构包含到了ZWindow之中,所以此this非彼this也。这一举应该写作:
            thunk.m_relproc = (int)proc – ((int)&thunk+sizeof(_WndProcThunk));
            将这句修改之后,窗口的创建仍然是失败的。这是由于你处理WM_NCCREATE之后返回了0,这就会导致CreateWindowEx失败。简单说来改正这个错误的方法是:
            你自己处理过的消息,根据MSDN上的说明来返回;
            不处理的消息,直接调用DefWindowProc返回。

            • At 2006.01.31 18:16, 诚心请教 said:

              谢谢老马,
              你说两个问题我都解决了,
              不过 我测试后 好像thunk里面把pthis转换的代码
              错了。。

              #include

              class ZWindow;

              ZWindow* g_pWnd = NULL;

              #pragma pack(push,1)
              struct _WndProcThunk
              {
              DWORD m_mov; // mov dword ptr [esp+0x4], pThis (esp+0x4 is hWnd)
              DWORD m_this;
              BYTE m_jmp; // jmp WndProc
              DWORD m_relproc; // 相对jmp
              };
              #pragma pack(pop)

              typedef LRESULT (CALLBACK* WNDPROC)(HWND, UINT, WPARAM, LPARAM);

              class ZWindow
              {
              public:
              HWND m_hWnd;

              _WndProcThunk thunk;

              ZWindow(HWND hWnd = 0) : m_hWnd(hWnd) { }

              void Init(WNDPROC proc, void* pThis)
              {
              thunk.m_mov = 0x042444C7; //C7 44 24 04
              thunk.m_this = (DWORD)pThis;
              thunk.m_jmp = 0xe9;
              thunk.m_relproc = (int)proc – ((int)&thunk+sizeof(_WndProcThunk));

              FlushInstructionCache(GetCurrentProcess(), &thunk, sizeof(thunk));
              }

              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); }

              HWND Create(LPCTSTR szClassName, LPCTSTR szTitle, HINSTANCE hInstance,
              HWND hWndParent = 0, DWORD dwStyle = WS_OVERLAPPEDWINDOW,
              DWORD dwExStyle = 0, HMENU hMenu = 0, int x = CW_USEDEFAULT,
              int y = CW_USEDEFAULT, int nWidth = CW_USEDEFAULT,
              int nHeight = CW_USEDEFAULT)
              {
              m_hWnd = ::CreateWindowEx(dwExStyle, szClassName, szTitle, dwStyle,
              x, y, nWidth, nHeight,
              hWndParent, hMenu, hInstance, NULL);

              return m_hWnd ;

              }

              virtual LRESULT OnPaint(WPARAM wParam, LPARAM lParam)
              {
              HDC hDC;
              PAINTSTRUCT ps;
              RECT rect;

              hDC = BeginPaint(&ps);
              GetClientRect(&rect);
              ::DrawText(hDC, “Hello world”, -1, &rect,
              DT_CENTER | DT_VCENTER | DT_SINGLELINE);
              EndPaint(&ps);
              return 0;
              }

              virtual LRESULT OnLButtonDown(WPARAM wParam, LPARAM lParam)
              {
              return 0;
              }

              virtual LRESULT OnKeyDown(WPARAM wParam, LPARAM lParam)
              {
              return 0;
              }

              static LRESULT CALLBACK StartWndProc(HWND hWnd, UINT uMsg,
              WPARAM wParam, LPARAM lParam)
              {
              ZWindow* pThis = g_pWnd ;
              pThis->m_hWnd = hWnd;
              pThis->Init(WindowProc, pThis);
              WNDPROC pProc = (WNDPROC)&(pThis->thunk);

              WNDPROC pOldProc = (WNDPROC)::SetWindowLong(hWnd, GWL_WNDPROC, (LONG)WindowProc);
              pProc(hWnd, uMsg, wParam, lParam);
              return 0;
              }

              static LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg,
              WPARAM wParam, LPARAM lParam)
              {

              ZWindow* pThis = g_pWnd ;
              // ZWindow* pThis = (ZWindow*)hWnd;
              pThis->ProcessWindowMessage(pThis->m_hWnd, uMsg, wParam, lParam) ;
              return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
              }

              BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg,
              WPARAM wParam, LPARAM lParam)
              {
              switch (uMsg)
              {
              case WM_PAINT:
              OnPaint(wParam, lParam);
              break;

              case WM_DESTROY:
              ::PostQuitMessage(0);
              break;
              }
              return 1;

              }
              };

              int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
              LPSTR lpCmdLine, int nCmdShow)
              {

              MSG msg;
              WNDCLASS wnd;
              char szAppName[] = “Hello world”;
              ZWindow zwnd1;
              hInstance = hInstance;

              wnd.cbClsExtra = NULL;
              wnd.cbWndExtra = NULL;
              wnd.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH);
              wnd.hCursor = LoadCursor(NULL, IDC_ARROW);
              wnd.hIcon = LoadIcon(NULL, IDI_APPLICATION);
              wnd.hInstance = hInstance;
              wnd.lpfnWndProc = ZWindow::StartWndProc;
              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 = &zwnd1;
              zwnd1.Create(szAppName, “Hell world”, hInstance);

              zwnd1.ShowWindow(nCmdShow);
              zwnd1.UpdateWindow();

              while (GetMessage(&msg, NULL, 0, 0))
              {
              DispatchMessage(&msg);
              }

              return msg.wParam;

              }

              • At 2006.02.28 13:41, gibit said:

                thunk.m_relproc = (int)proc – ((int)&thunk+sizeof(_WndProcThunk));不是很严谨,应该为
                thunk.m_relproc = (int)proc – ((int)&(((ZWindow*)pThis)->thunk)+sizeof(_WndProcThunk));

                • At 2006.02.28 13:58, 李马 said:

                  To gibit:
                  感谢您的提醒,其实对于评论中的例子,如果真的要把thunk封装在ZWindow之中,可以将Init这样写:
                  void Init(WNDPROC proc)
                  {
                  thunk.m_mov = 0x042444C7; //C7 44 24 04
                  thunk.m_this = (DWORD)this;
                  thunk.m_jmp = 0xe9;
                  thunk.m_relproc = (int)proc – ((int)&thunk+sizeof(_WndProcThunk));

                  FlushInstructionCache(GetCurrentProcess(), &thunk, sizeof(thunk));
                  }
                  然后在StartWndProc中这样处理:
                  pThis->Init(WindowProc);
                  是前我的回复存在着逻辑冗余和隐患,不应提供那个多余的参数。再次感谢您的提醒。

                  (Required)
                  (Required, will not be published)