ATL中采用了一种动态生成机器指令的方式进行窗口句柄与窗口对象进行关联,以是详细分析:

CWindowImpl会在第一次调用Create时注册窗口类,该窗口类是的信息是在CWindowImpl的子类中使用

DECLARE_WND_CLASS定义的,该宏会为CWindowImpl的子类定义一个静态成员函数GetWndClassInfo,

该函数返回一个CWndClassInfo结构体,其中包含了WNDCLASSEX,用于指定该类的窗口类注册时所用的

WNDCLASSEX结构。

在DECLARE_WND_CLASS指定的默认的窗口过程是StartWindowProc,该函数是CWindowImplBaseT

的静态成员函数,用于第一次收到消息时将窗口对象与窗口句柄关联(参见下文),StartWindowProc函数

定义如下所示:

  1. template <class TBase, class TWinTraits>
  2. LRESULT CALLBACK CWindowImplBaseT< TBase, TWinTraits >::StartWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  3. {
  4. CWindowImplBaseT< TBase, TWinTraits >* pThis = (CWindowImplBaseT< TBase, TWinTraits >*)_AtlWinModule.ExtractCreateWndData();
  5. ATLASSERT(pThis != NULL);
  6. if(!pThis)
  7. {
  8. return 0;
  9. }
  10. pThis->m_hWnd = hWnd;
  11.  
  12. // Initialize the thunk. This is allocated in CWindowImplBaseT::Create,
  13. // so failure is unexpected here.
  14.  
  15. pThis->m_thunk.Init(pThis->GetWindowProc(), pThis);
  16. WNDPROC pProc = pThis->m_thunk.GetWNDPROC();
  17. WNDPROC pOldProc = (WNDPROC)::SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)pProc);
  18. #ifdef _DEBUG
  19. // check if somebody has subclassed us already since we discard it
  20. if(pOldProc != StartWindowProc)
  21. ATLTRACE(atlTraceWindowing, 0, _T("Subclassing through a hook discarded.\n"));
  22. #else
  23. (pOldProc); // avoid unused warning
  24. #endif
  25. return pProc(hWnd, uMsg, wParam, lParam);
  26. }

函数首先通过_AtlWinModule.ExtractCreateWndData()获取到当前正在创建的窗口的CWindowImpl对象的this指针,

实际上,在_AtlWinModule中保存了一个链表,用于保存当前正在创建窗口的窗口对象的指针,当调用CWindowImpl的

Create方法时将该对象的指针到链表中,在StartWindowProc时取出,这个链表是根据ThreadID进行关联的,所以可以

保证在多个线程创建窗口是,可以在StartWindowProc取到正确的this指针。

取到对象的this指针后,首先对其m_hWnd赋值,然后初始化该对象的thunk成员(用于实现窗口和窗口对象关联的关键

对象)。该结构定义如下:

  1. class CWndProcThunk
  2. {
  3. public:
  4. _AtlCreateWndData cd;
  5. CStdCallThunk thunk;
  6.  
  7. BOOL Init(WNDPROC proc, void* pThis)
  8. {
  9. return thunk.Init((DWORD_PTR)proc, pThis);
  10. }
  11. WNDPROC GetWNDPROC()
  12. {
  13. return (WNDPROC)thunk.GetCodeAddress();
  14. }
  15. };
  16.  
  17. struct _AtlCreateWndData
  18. {
  19. void* m_pThis;
  20. DWORD m_dwThreadID;
  21. _AtlCreateWndData* m_pNext;
  22. };
  23.  
  24. #if defined(_M_IX86) || defined (_M_AMD64)
  25.  
  26. #pragma pack(push,8)
  27. class CDynamicStdCallThunk
  28. {
  29. public:
  30. _stdcallthunk *pThunk;
  31.  
  32. CDynamicStdCallThunk()
  33. {
  34. pThunk = NULL;
  35. }
  36.  
  37. ~CDynamicStdCallThunk()
  38. {
  39. if (pThunk)
  40. {
  41. delete pThunk;
  42. }
  43. }
  44.  
  45. BOOL Init(DWORD_PTR proc, void *pThis)
  46. {
  47. if (pThunk == NULL)
  48. {
  49. pThunk = new _stdcallthunk;
  50. if (pThunk == NULL)
  51. {
  52. return FALSE;
  53. }
  54. }
  55. return pThunk->Init(proc, pThis);
  56. }
  57.  
  58. void* GetCodeAddress()
  59. {
  60. return pThunk->GetCodeAddress();
  61. }
  62. };
  63.  
  64. #pragma pack(pop)
  65. typedef CDynamicStdCallThunk CStdCallThunk;
  66. #else
  67. typedef _stdcallthunk CStdCallThunk;
  68. #endif

CWndProcThunk中的成员cd就是上文所述的_AtlWinModule.ExtractCreateWndData()中保存的创建信息,成员thunk用于窗口和窗口对象关联。定义如下:

  1. struct _stdcallthunk
  2. {
  3. DWORD m_mov; // mov dword ptr [esp+0x4], pThis (esp+0x4 is hWnd)
  4. DWORD m_this; //
  5. BYTE m_jmp; // jmp WndProc
  6. DWORD m_relproc; // relative jmp
  7. BOOL Init(DWORD_PTR proc, void* pThis)
  8. {
  9. m_mov = 0x042444C7; //C7 44 24 0C
  10. m_this = PtrToUlong(pThis);
  11. m_jmp = 0xe9;
  12. m_relproc = DWORD((INT_PTR)proc - ((INT_PTR)this+sizeof(_stdcallthunk)));
  13. // write block from data cache and
  14. // flush from instruction cache
  15. FlushInstructionCache(GetCurrentProcess(), this, sizeof(_stdcallthunk));
  16. return TRUE;
  17. }
  18. //some thunks will dynamically allocate the memory for the code
  19. void* GetCodeAddress()
  20. {
  21. return this;
  22. }
  23. void* operator new(size_t)
  24. {
  25. return __AllocStdCallThunk();
  26. }
  27. void operator delete(void* pThunk)
  28. {
  29. __FreeStdCallThunk(pThunk);
  30. }
  31. };

 该结构实际上是一段代码,用于替换真正的窗口过程,该代码被StartWindowProc通过SetWindowLongPtr设置为窗口的窗口过程,由于系统调用窗口过程是采用的是stdcall,

所以会将窗口过程的参数从逆序压栈,窗口过程的原型如下:

  1. typedef LRESULT (CALLBACK* WNDPROC)(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)

 所以在调用m_thunk时栈内容如下:

[esp + 00]-->|            |

[esp + 04]-->|  HWND |

[esp + 08]-->|message|

[esp + 0C]-->|wParam |

[esp + 10]-->|lParam   |

thunk中的代码等效于

mov dword ptr [esp+0x4], pThis

jmp WndProc

实际上就是把栈上hwnd参数修改为窗口对象的this指针,并跳转到窗口对象的WindowProc函数

(默认为CWindowImplBaseT< TBase, TWinTraits >::WindowProc,是个静态函数)。

而thunk的成员pThis和m_realproc是在StartWindowProc中初始化化的。

这样在下次系统调用窗口过程时,就会支持thunk的代码,并跳转到指定的WindowProc函数中,

在WindowProc中会将hwnd转化为this指针,并调用对应的对象进行消息处理。

CWindowImplBaseT< TBase, TWinTraits >::WindowProc定义如下:

  1. template <class TBase, class TWinTraits>
  2. LRESULT CALLBACK CWindowImplBaseT< TBase, TWinTraits >::WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  3. {
  4. CWindowImplBaseT< TBase, TWinTraits >* pThis = (CWindowImplBaseT< TBase, TWinTraits >*)hWnd;
  5. // set a ptr to this message and save the old value
  6. _ATL_MSG msg(pThis->m_hWnd, uMsg, wParam, lParam);
  7. const _ATL_MSG* pOldMsg = pThis->m_pCurrentMsg;
  8. pThis->m_pCurrentMsg = &msg;
  9. // pass to the message map to process
  10. LRESULT lRes;
  11. BOOL bRet = pThis->ProcessWindowMessage(pThis->m_hWnd, uMsg, wParam, lParam, lRes, 0);
  12. // restore saved value for the current message
  13. ATLASSERT(pThis->m_pCurrentMsg == &msg);
  14.  
  15. // do the default processing if message was not handled
  16. if(!bRet)
  17. {
  18. if(uMsg != WM_NCDESTROY)
  19. lRes = pThis->DefWindowProc(uMsg, wParam, lParam);
  20. else
  21. {
  22. // unsubclass, if needed
  23. LONG_PTR pfnWndProc = ::GetWindowLongPtr(pThis->m_hWnd, GWLP_WNDPROC);
  24. lRes = pThis->DefWindowProc(uMsg, wParam, lParam);
  25. if(pThis->m_pfnSuperWindowProc != ::DefWindowProc && ::GetWindowLongPtr(pThis->m_hWnd, GWLP_WNDPROC) == pfnWndProc)
  26. ::SetWindowLongPtr(pThis->m_hWnd, GWLP_WNDPROC, (LONG_PTR)pThis->m_pfnSuperWindowProc);
  27. // mark window as destryed
  28. pThis->m_dwState |= WINSTATE_DESTROYED;
  29. }
  30. }
  31. if((pThis->m_dwState & WINSTATE_DESTROYED) && pOldMsg== NULL)
  32. {
  33. // clear out window handle
  34. HWND hWndThis = pThis->m_hWnd;
  35. pThis->m_hWnd = NULL;
  36. pThis->m_dwState &= ~WINSTATE_DESTROYED;
  37. // clean up after window is destroyed
  38. pThis->m_pCurrentMsg = pOldMsg;
  39. pThis->OnFinalMessage(hWndThis);
  40. }else {
  41. pThis->m_pCurrentMsg = pOldMsg;
  42. }
  43. return lRes;
  44. }

  

ATL中窗口句柄与窗口过程的关联方法的更多相关文章

  1. JS 在open打开的子窗口页面中调用父窗口页面的JS方法

    需求的情景如下: 1:做新增或修改等操作的时候打开一个新的浏览器窗口(使用window.open(参数等)方法) 2:在新增或修改等的页面上有返回按钮.重置按钮.保存按钮,对于返回就直接关闭此窗口(使 ...

  2. 从普通函数到对象方法 ------Windows窗口过程的面向对象封装

    原文地址:http://blog.csdn.net/linzhengqun/article/details/1451088 从普通函数到对象方法 ------Windows窗口过程的面向对象封装 开始 ...

  3. win32程序通过LPCREATESTRUCT中的lpCreateParams传递参数给窗口过程函数

    win32窗口程序中如果需要给窗口过程函数传递自定义参数,可以通过LPCREATESTRUCT结构体中的lpCreateParams进行传递. 创建窗口实例函数: m_hWnd = CreateWin ...

  4. C#在父窗口中调用子窗口的过程(无法访问已释放的对象)异常,不存在从对象类型System.Windows.Forms.DateTimePicker到已知的托管提供程序本机类型的映射。

    一:C#在父窗口中调用子窗口的过程(无法访问已释放的对象)异常 其实,这个问题与C#的垃圾回收有关.垃圾回收器管 理所有的托管对象,所有需要托管数据的.NET语言(包括 C#)都受运行库的 垃圾回收器 ...

  5. (转)C#在父窗口中调用子窗口的过程(无法访问已释放的对象)

    C#在父窗口中调用子窗口的过程: 1. 创建子窗口对象 2. 显示子窗口对象   笔者的程序中,主窗体MainFrm通过菜单调用子窗口ChildFrm.在窗体中定义了子窗口对象,然后在菜单项点击事件中 ...

  6. win32编程中消息循环和WndProc()窗口过程函数

    原文地址:https://blog.csdn.net/zxxSsdsd/article/details/45504383 在win32程序的消息循环函数中  while (GetMessage (&a ...

  7. 理解ATL中的一些汇编代码(通过Thunk技术来调用类成员函数)

    我们知道ATL(活动模板库)是一套很小巧高效的COM开发库,它本身的核心文件其实没几个,COM相关的(主要是atlbase.h, atlcom.h),另外还有一个窗口相关的(atlwin.h), 所以 ...

  8. C#调用WinAPI及窗口过程

    C#调用WINAPI及Windows窗口消息的发与送 最近在做一个餐饮项目(C#Winform),其中有一块是做点菜宝接口的对接,点菜宝的厂商提供了一个WX.exe的驱动程序,这个驱动程序无直接打开, ...

  9. MSG结构体和WndProc窗口过程详解

    MSG结构体和WndProc窗口过程对于Windows编程非常重要,如果不了解它们,可以说就没有学会Windows编程. MSG结构体 MSG 结构体用来表示一条消息,各个字段的含义如下: typed ...

随机推荐

  1. Java中避免表单重复提交

    表单的重复提交: 没有完整的进行一次,先请求表单页面->再提交表单过程而完成数据提交 造成的根本原因: 没有完整的进行一次,先请求表单页面->再提交表单过程. 造成重复提交的现象: 由于服 ...

  2. easycwmp的编译

    原创作品,转载请注明出处,严禁非法转载. copyright:weishusheng   2015.3.18 email:642613208@qq.com 注:此处的编译指的是直接用系统自带的gcc编 ...

  3. SQL Server 连接字符串备忘

    今天把服务器上的远程访问关了,把连接字符串中的IP地址改成了.,然后就一直连不上.弄了半天,原来是本地连接时非默认实例,不能带端口号. 1.原来的连接服务器地址是:192.168.0.1SQL2005 ...

  4. DBA-mysql-表

    create table  student( id int(4) not null, name char(20) not null, age int(3) not null default '0', ...

  5. 用word制作电子书最简捷模式 支持epub和mobi目录

    因为制作一本OCR的电子书,转到word编辑排版后,用calibre转成mobi发现没有目录,在网上查了资料研究了一下,终于解决了目录问题,根本不用将word文档转换为什么htm或txt,尤其是转换t ...

  6. oracle如何写包

    一:如何使用FOR循环二:如何使用拼接语句 EXECUTE IMMEDIATE v_sql INTO v_WORK_ORDERID;三:如何定义记录类型做为变量,用于存储及查询 CREATE OR R ...

  7. zepto.js使用前注意

    API:http://www.css88.com/doc/zeptojs_api/ 一.建议:不要从官网下载,而是从 Github 下载了源代码之后自己 Build 一个版本,这样你可以自行挑选适合的 ...

  8. 服务端性能测试工具校验v1.1

    服务端性能测试工具校验v1.1 更新说明: 1.精简CRT运行库支持. 2.添加响应模拟测试,校验压力测试工具的响应时间统计准确性. 3.大并发请求请降低延迟时间 WEIMJSAM原创,转载请注明出处 ...

  9. Oracle 违反协议 OALL8 处于不一致状态

    http://blog.sina.com.cn/s/blog_a45aac720100yu3h.html ERROR-util.JDBCExceptionReporter>: 违反协议ERROR ...

  10. 【MySQL】锁入门

    要做的完全掌握MySQL/InnoDB的加锁规则,甚至是其他任何数据库的加锁规则,需要具备以下的一些知识点 了解数据库的一些基本理论知识:数据的存储格式 (堆组织表 vs 聚簇索引表):并发控制协议 ...