Subclass.cpp
上传用户:zhoushen
上传日期:2022-06-15
资源大小:84k
文件大小:7k
源码类别:

对话框与窗口

开发平台:

Visual C++

  1. ////////////////////////////////////////////////////////////////
  2. // Copyright 1996 Microsoft Systems Journal. 
  3. // If this program works, it was written by Paul DiLascia.
  4. // If not, I don't know who wrote it.
  5. //
  6. // CSubclassWnd is a generic class for hooking another window's messages.
  7. #include "StdAfx.h"
  8. #include "Subclass.h"
  9. #ifdef _DEBUG
  10. #define new DEBUG_NEW
  11. #undef THIS_FILE
  12. static char THIS_FILE[] = __FILE__;
  13. #endif
  14. //////////////////
  15. // The message hook map is derived from CMapPtrToPtr, which associates
  16. // a pointer with another pointer. It maps an HWND to a CSubclassWnd, like
  17. // the way MFC's internal maps map HWND's to CWnd's. The first hook
  18. // attached to a window is stored in the map; all other hooks for that
  19. // window are then chained via CSubclassWnd::m_pNext.
  20. //
  21. class CSubclassWndMap : private CMapPtrToPtr 
  22. {
  23. public:
  24. CSubclassWndMap();
  25. ~CSubclassWndMap();
  26. static CSubclassWndMap& GetHookMap();
  27. void Add(HWND hwnd, CSubclassWnd* pMsgHook);
  28. void Remove(CSubclassWnd* pMsgHook);
  29. void RemoveAll(HWND hwnd);
  30. CSubclassWnd* Lookup(HWND hwnd);
  31. };
  32. // This trick is used so the hook map isn't
  33. // instantiated until someone actually requests it.
  34. //
  35. #define theHookMap (CSubclassWndMap::GetHookMap())
  36. IMPLEMENT_DYNAMIC(CSubclassWnd, CWnd);
  37. CSubclassWnd::CSubclassWnd()
  38. {
  39. m_pNext = NULL;
  40. m_pOldWndProc = NULL;
  41. m_pWndHooked  = NULL;
  42. }
  43. CSubclassWnd::~CSubclassWnd()
  44. {
  45. ASSERT(m_pWndHooked==NULL); // can't destroy while still hooked!
  46. ASSERT(m_pOldWndProc==NULL);
  47. }
  48. //////////////////
  49. // Hook a window.
  50. // This installs a new window proc that directs messages to the CSubclassWnd.
  51. // pWnd=NULL to remove.
  52. //
  53. BOOL CSubclassWnd::HookWindow(CWnd* pWnd)
  54. {
  55. if (pWnd) 
  56. {
  57. // Hook the window
  58. ASSERT(m_pWndHooked==NULL);
  59. HWND hwnd = pWnd->m_hWnd;
  60. ASSERT(hwnd && ::IsWindow(hwnd));
  61. theHookMap.Add(hwnd, this); // Add to map of hooks
  62. else 
  63. {
  64. // Unhook the window
  65. ASSERT(m_pWndHooked!=NULL);
  66. theHookMap.Remove(this); // Remove from map
  67. m_pOldWndProc = NULL;
  68. }
  69. m_pWndHooked = pWnd;
  70. return TRUE;
  71. }
  72. //////////////////
  73. // Window proc-like virtual function which specific CSubclassWnds will
  74. // override to do stuff. Default passes the message to the next hook; 
  75. // the last hook passes the message to the original window.
  76. // You MUST call this at the end of your WindowProc if you want the real
  77. // window to get the message. This is just like CWnd::WindowProc, except that
  78. // a CSubclassWnd is not a window.
  79. //
  80. LRESULT CSubclassWnd::WindowProc(UINT msg, WPARAM wp, LPARAM lp)
  81. {
  82. ASSERT(m_pOldWndProc);
  83. return m_pNext ? m_pNext->WindowProc(msg, wp, lp) :
  84. ::CallWindowProc(m_pOldWndProc, m_pWndHooked->m_hWnd, msg, wp, lp);
  85. }
  86. //////////////////
  87. // Like calling base class WindowProc, but with no args, so individual
  88. // message handlers can do the default thing. Like CWnd::Default
  89. //
  90. LRESULT CSubclassWnd::Default()
  91. {
  92. // MFC stores current MSG in thread state
  93. MSG& curMsg = AfxGetThreadState()->m_lastSentMsg;
  94. // Note: must explicitly call CSubclassWnd::WindowProc to avoid infinte
  95. // recursion on virtual function
  96. return CSubclassWnd::WindowProc(curMsg.message, curMsg.wParam, curMsg.lParam);
  97. }
  98. //////////////////
  99. // Subclassed window proc for message hooks. Replaces AfxWndProc (or whatever
  100. // else was there before.)
  101. //
  102. LRESULT CALLBACK
  103. HookWndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
  104. {
  105. #ifdef _USRDLL
  106. // If this is a DLL, need to set up MFC state
  107. AFX_MANAGE_STATE(AfxGetStaticModuleState());
  108. #endif
  109. // Set up MFC message state just in case anyone wants it
  110. // This is just like AfxCallWindowProc, but we can't use that because
  111. // a CSubclassWnd is not a CWnd.
  112. //
  113. MSG& curMsg = AfxGetThreadState()->m_lastSentMsg;
  114. MSG  oldMsg = curMsg;   // save for nesting
  115. curMsg.hwnd = hwnd;
  116. curMsg.message = msg;
  117. curMsg.wParam  = wp;
  118. curMsg.lParam  = lp;
  119. // Get hook object for this window. Get from hook map
  120. CSubclassWnd* pMsgHook = theHookMap.Lookup(hwnd);
  121. ASSERT(pMsgHook);
  122. LRESULT lr;
  123. if (msg==WM_NCDESTROY) 
  124. {
  125. // Window is being destroyed: unhook all hooks (for this window)
  126. // and pass msg to orginal window proc
  127. //
  128. WNDPROC wndproc = pMsgHook->m_pOldWndProc;
  129. theHookMap.RemoveAll(hwnd);
  130. lr = ::CallWindowProc(wndproc, hwnd, msg, wp, lp);
  131. else 
  132. {
  133. // pass to msg hook
  134. lr = pMsgHook->WindowProc(msg, wp, lp);  
  135. }
  136. curMsg = oldMsg; // pop state
  137. return lr;
  138. }
  139. ////////////////////////////////////////////////////////////////
  140. // CSubclassWndMap implementation
  141. CSubclassWndMap::CSubclassWndMap()
  142. {
  143. }
  144. CSubclassWndMap::~CSubclassWndMap()
  145. {
  146. ASSERT(IsEmpty()); // all hooks should be removed!
  147. }
  148. //////////////////
  149. // Get the one and only global hook map
  150. // 
  151. CSubclassWndMap& CSubclassWndMap::GetHookMap()
  152. {
  153. // By creating theMap here, C++ doesn't instantiate it until/unless
  154. // it's ever used! This is a good trick to use in C++, to
  155. // instantiate/initialize a static object the first time it's used.
  156. //
  157. static CSubclassWndMap theMap;
  158. return theMap;
  159. }
  160. /////////////////
  161. // Add hook to map; i.e., associate hook with window
  162. //
  163. void CSubclassWndMap::Add(HWND hwnd, CSubclassWnd* pMsgHook)
  164. {
  165. ASSERT(hwnd && ::IsWindow(hwnd));
  166. // Add to front of list
  167. pMsgHook->m_pNext = Lookup(hwnd);
  168. SetAt(hwnd, pMsgHook);
  169. if (pMsgHook->m_pNext==NULL) 
  170. {
  171. // If this is the first hook added, subclass the window
  172. pMsgHook->m_pOldWndProc = 
  173. (WNDPROC)SetWindowLong(hwnd, GWL_WNDPROC, (DWORD)HookWndProc);
  174. else 
  175. {
  176. // just copy wndproc from next hook
  177. pMsgHook->m_pOldWndProc = pMsgHook->m_pNext->m_pOldWndProc;
  178. }
  179. ASSERT(pMsgHook->m_pOldWndProc);
  180. }
  181. //////////////////
  182. // Remove hook from map
  183. //
  184. void CSubclassWndMap::Remove(CSubclassWnd* pUnHook)
  185. {
  186. HWND hwnd = pUnHook->m_pWndHooked->GetSafeHwnd();
  187. ASSERT(hwnd && ::IsWindow(hwnd));
  188. CSubclassWnd* pHook = Lookup(hwnd);
  189. ASSERT(pHook);
  190. if (pHook==pUnHook) 
  191. {
  192. // hook to remove is the one in the hash table: replace w/next
  193. if (pHook->m_pNext)
  194. SetAt(hwnd, pHook->m_pNext);
  195. else 
  196. {
  197. // This is the last hook for this window: restore wnd proc
  198. RemoveKey(hwnd);
  199. SetWindowLong(hwnd, GWL_WNDPROC, (DWORD)pHook->m_pOldWndProc);
  200. }
  201. else 
  202. {
  203. // Hook to remove is in the middle: just remove from linked list
  204. while (pHook->m_pNext!=pUnHook)
  205. pHook = pHook->m_pNext;
  206. ASSERT(pHook && pHook->m_pNext==pUnHook);
  207. pHook->m_pNext = pUnHook->m_pNext;
  208. }
  209. }
  210. //////////////////
  211. // Remove all the hooks for a window
  212. //
  213. void CSubclassWndMap::RemoveAll(HWND hwnd)
  214. {
  215. CSubclassWnd* pMsgHook;
  216. while ((pMsgHook = Lookup(hwnd))!=NULL)
  217. pMsgHook->HookWindow(NULL); // (unhook)
  218. }
  219. /////////////////
  220. // Find first hook associate with window
  221. //
  222. CSubclassWnd* CSubclassWndMap::Lookup(HWND hwnd)
  223. {
  224. CSubclassWnd* pFound = NULL;
  225. if (!CMapPtrToPtr::Lookup(hwnd, (void*&)pFound))
  226. return NULL;
  227. ASSERT_KINDOF(CSubclassWnd, pFound);
  228. return pFound;
  229. }