Subclass.cpp
上传用户:weijiexitu
上传日期:2007-01-18
资源大小:54k
文件大小:10k
源码类别:

菜单

开发平台:

WINDOWS

  1. ////////////////////////////////////////////////////////////////
  2. // 1997 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 CSubclassWnd
  18. // attached to a window is stored in the map; all other CSubclassWnd's 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* pSubclassWnd);
  28. void Remove(CSubclassWnd* pSubclassWnd);
  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. #define theSafeMap (CSubclassWnd::GetValidMap())
  37. ISubclassCallback* CSubclassWnd::s_pCallback = NULL;
  38. CSubclassWnd::CSubclassWnd()
  39. {
  40. m_pNext = NULL;
  41. m_pOldWndProc = NULL;
  42. m_hWndHooked  = NULL;
  43. m_pSubclasser = NULL;
  44. }
  45. CSubclassWnd::~CSubclassWnd()
  46. {
  47. if (m_hWndHooked)
  48. HookWindow((HWND)NULL);
  49. ASSERT(m_hWndHooked==NULL); // can't destroy while still hooked!
  50. ASSERT(m_pOldWndProc==NULL);
  51. }
  52. //////////////////
  53. // Hook a window.
  54. // This installs a new window proc that directs messages to the CSubclassWnd.
  55. // pWnd=NULL to remove.
  56. //
  57. BOOL CSubclassWnd::HookWindow(HWND hWnd, CSubclasser* pSubclasser)
  58. {
  59. if (hWnd) 
  60. {
  61. // Hook the window
  62. ASSERT(m_hWndHooked == NULL);
  63. if (m_hWndHooked) // only once
  64. return FALSE;
  65. ASSERT(::IsWindow(hWnd));
  66. if (!::IsWindow(hWnd))
  67. return FALSE;
  68. m_hWndHooked = hWnd;
  69. theHookMap.Add(m_hWndHooked, this); // Add to map of hooks
  70. theSafeMap.SetAt((void*)this, NULL);
  71. m_pSubclasser = pSubclasser;
  72. else 
  73. {
  74. // Unhook the window
  75. if (m_hWndHooked)
  76. theHookMap.Remove(this); // Remove from map
  77. theSafeMap.RemoveKey((void*)this);
  78. m_pOldWndProc = NULL;
  79. m_pSubclasser = NULL;
  80. m_hWndHooked = NULL;
  81. m_pNext = NULL;
  82. }
  83. return TRUE;
  84. }
  85. //////////////////
  86. // Window proc-like virtual function which specific CSubclassWnds will
  87. // override to do stuff. Default passes the message to the next hook; 
  88. // the last hook passes the message to the original window.
  89. // You MUST call this at the end of your WindowProc if you want the real
  90. // window to get the message. This is just like CWnd::WindowProc, except that
  91. // a CSubclassWnd is not a window.
  92. //
  93. LRESULT CSubclassWnd::WindowProc(HWND hRealWnd, UINT msg, WPARAM wp, LPARAM lp)
  94. {
  95. if (!::IsWindow(m_hWndHooked))
  96. return 0;
  97. ASSERT(m_pOldWndProc);
  98. if (m_pNext)
  99. {
  100. if (m_pNext->m_pSubclasser)
  101. return m_pNext->m_pSubclasser->ScWindowProc(m_hWndHooked, msg, wp, lp);
  102. else
  103. return m_pNext->WindowProc(m_hWndHooked, msg, wp, lp);
  104. }
  105. else
  106. return ::CallWindowProc(m_pOldWndProc, m_hWndHooked, msg, wp, lp);
  107. }
  108. //////////////////
  109. // Like calling base class WindowProc, but with no args, so individual
  110. // message handlers can do the default thing. Like CWnd::Default
  111. //
  112. LRESULT CSubclassWnd::Default()
  113. {
  114. if (!::IsWindow(m_hWndHooked))
  115. return 0;
  116. // MFC stores current MSG in thread state
  117. MSG& curMsg = AfxGetThreadState()->m_lastSentMsg;
  118. // Note: must explicitly call CSubclassWnd::WindowProc to avoid infinite
  119. // recursion on virtual function
  120. return CSubclassWnd::WindowProc(m_hWndHooked, curMsg.message, curMsg.wParam, curMsg.lParam);
  121. }
  122. //////////////////
  123. // Subclassed window proc for message hooks. Replaces AfxWndProc (or whatever
  124. // else was there before.)
  125. //
  126. LRESULT CALLBACK CSubclassWnd::HookWndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
  127. {
  128. #ifdef _USRDLL
  129. // If this is a DLL, need to set up MFC state
  130. AFX_MANAGE_STATE(AfxGetStaticModuleState());
  131. #endif
  132. // Set up MFC message state just in case anyone wants it
  133. // This is just like AfxCallWindowProc, but we can't use that because
  134. // a CSubclassWnd is not a CWnd.
  135. //
  136. MSG& curMsg = AfxGetThreadState()->m_lastSentMsg;
  137. MSG  oldMsg = curMsg;   // save for nesting
  138. curMsg.hwnd = hwnd;
  139. curMsg.message = msg;
  140. curMsg.wParam  = wp;
  141. curMsg.lParam  = lp;
  142. // Get hook object for this window. Get from hook map
  143. CSubclassWnd* pSubclassWnd = theHookMap.Lookup(hwnd);
  144. if (!pSubclassWnd)
  145. return 0;
  146. // see if this is a re-entrant call
  147. BOOL bInHookWndProc = FALSE;
  148. VERIFY (theSafeMap.Lookup((void*)pSubclassWnd, (void*&)bInHookWndProc));
  149. ASSERT (!bInHookWndProc);
  150. LRESULT lr;
  151. if (msg == WM_NCDESTROY) 
  152. {
  153. #ifdef _DEBUG
  154. char szClass[30];
  155. GetClassName(hwnd, szClass, 30);
  156. TRACE ("CSubclassWnd::HookWndProc(%s, WM_NCDESTROY)n", szClass);
  157. #endif
  158. // Window is being destroyed: unhook all hooks (for this window)
  159. // and pass msg to orginal window proc
  160. WNDPROC wndproc = pSubclassWnd->m_pOldWndProc;
  161. theHookMap.RemoveAll(hwnd);
  162. lr = ::CallWindowProc(wndproc, hwnd, msg, wp, lp);
  163. if (s_pCallback)
  164. s_pCallback->PostNcDestroy(hwnd);
  165. else 
  166. {
  167. if (pSubclassWnd->m_pSubclasser)
  168. {
  169. // pass to msg hook
  170. lr = pSubclassWnd->m_pSubclasser->ScWindowProc(pSubclassWnd->GetHwnd(), msg, wp, lp);
  171. }
  172. else
  173. {
  174. // pass to msg hook
  175. lr = pSubclassWnd->WindowProc(pSubclassWnd->GetHwnd(), msg, wp, lp);
  176. }
  177. }
  178. theSafeMap[(void*)pSubclassWnd] = NULL;
  179. curMsg = oldMsg; // pop state
  180. return lr;
  181. }
  182. BOOL CSubclassWnd::PostMessage(UINT message, WPARAM wParam, LPARAM lParam) const
  183. {
  184. if (IsValid())
  185. return ::PostMessage(m_hWndHooked, message, wParam, lParam);
  186. // else
  187. return FALSE;
  188. }
  189. BOOL CSubclassWnd::SendMessage(UINT message, WPARAM wParam, LPARAM lParam) const
  190. {
  191. if (IsValid())
  192. return ::SendMessage(m_hWndHooked, message, wParam, lParam);
  193. // else
  194. return FALSE;
  195. }
  196. CMapPtrToPtr& CSubclassWnd::GetValidMap()
  197. {
  198. // By creating theMap here, C++ doesn't instantiate it until/unless
  199. // it's ever used! This is a good trick to use in C++, to
  200. // instantiate/initialize a static object the first time it's used.
  201. //
  202. static CMapPtrToPtr theMap;
  203. return theMap;
  204. }
  205. BOOL CSubclassWnd::IsValid(const CSubclassWnd* pScWnd)
  206. {
  207. void* pResult;
  208. return theSafeMap.Lookup((void*)pScWnd, pResult);
  209. }
  210. void CSubclassWnd::ClientToWindow(LPRECT pRect)
  211. {
  212. ::ClientToScreen(m_hWndHooked, (LPPOINT)pRect);
  213. ::ClientToScreen(m_hWndHooked, ((LPPOINT)pRect) + 1);
  214. ::MapWindowPoints(NULL, m_hWndHooked, (LPPOINT)pRect, 1);
  215. ::MapWindowPoints(NULL, m_hWndHooked, ((LPPOINT)pRect) + 1, 1);
  216. }
  217. void CSubclassWnd::ScreenToClient(LPRECT pRect)
  218. {
  219. ::ScreenToClient(m_hWndHooked, (LPPOINT)pRect);
  220. ::ScreenToClient(m_hWndHooked, ((LPPOINT)pRect) + 1);
  221. }
  222. void CSubclassWnd::ClientToScreen(LPRECT pRect)
  223. {
  224. ::ClientToScreen(m_hWndHooked, (LPPOINT)pRect);
  225. ::ClientToScreen(m_hWndHooked, ((LPPOINT)pRect) + 1);
  226. }
  227. ////////////////////////////////////////////////////////////////
  228. // CSubclassWndMap implementation
  229. CSubclassWndMap::CSubclassWndMap()
  230. {
  231. }
  232. CSubclassWndMap::~CSubclassWndMap()
  233. {
  234. // ASSERT(IsEmpty()); // all hooks should be removed!
  235. }
  236. //////////////////
  237. // Get the one and only global hook map
  238. // 
  239. CSubclassWndMap& CSubclassWndMap::GetHookMap()
  240. {
  241. // By creating theMap here, C++ doesn't instantiate it until/unless
  242. // it's ever used! This is a good trick to use in C++, to
  243. // instantiate/initialize a static object the first time it's used.
  244. //
  245. static CSubclassWndMap theMap;
  246. return theMap;
  247. }
  248. /////////////////
  249. // Add hook to map; i.e., associate hook with window
  250. //
  251. void CSubclassWndMap::Add(HWND hwnd, CSubclassWnd* pSubclassWnd)
  252. {
  253. ASSERT(hwnd && ::IsWindow(hwnd));
  254. // Add to front of list
  255. pSubclassWnd->m_pNext = Lookup(hwnd);
  256. SetAt(hwnd, pSubclassWnd);
  257. if (pSubclassWnd->m_pNext == NULL) 
  258. {
  259. // If this is the first hook added, subclass the window
  260. WNDPROC wndProc = (WNDPROC)GetWindowLong(hwnd, GWL_WNDPROC);
  261. pSubclassWnd->m_pOldWndProc = 
  262. (WNDPROC)SetWindowLong(hwnd, GWL_WNDPROC, (DWORD)CSubclassWnd::HookWndProc);
  263. else 
  264. {
  265. // just copy wndproc from next hook
  266. pSubclassWnd->m_pOldWndProc = pSubclassWnd->m_pNext->m_pOldWndProc;
  267. }
  268. ASSERT(pSubclassWnd->m_pOldWndProc);
  269. }
  270. //////////////////
  271. // Remove hook from map
  272. //
  273. void CSubclassWndMap::Remove(CSubclassWnd* pUnHook)
  274. {
  275. HWND hwnd = pUnHook->m_hWndHooked;
  276. ASSERT(hwnd && ::IsWindow(hwnd));
  277. CSubclassWnd* pHook = Lookup(hwnd);
  278. // ASSERT(pHook);
  279. if (!pHook)
  280. return;
  281. if (pHook == pUnHook) 
  282. {
  283. // hook to remove is the one in the hash table: replace w/next
  284. if (pHook->m_pNext)
  285. SetAt(hwnd, pHook->m_pNext);
  286. else 
  287. {
  288. // This is the last hook for this window: restore wnd proc
  289. RemoveKey(hwnd);
  290. SetWindowLong(hwnd, GWL_WNDPROC, (DWORD)pHook->m_pOldWndProc);
  291. }
  292. else 
  293. {
  294. // Hook to remove is in the middle: just remove from linked list
  295. while (pHook->m_pNext && pHook->m_pNext != pUnHook)
  296. pHook = pHook->m_pNext;
  297. if (pHook)
  298. {
  299. ASSERT(pHook->m_pNext == pUnHook);
  300. pHook->m_pNext = pUnHook->m_pNext;
  301. }
  302. }
  303. }
  304. //////////////////
  305. // Remove all the hooks for a window
  306. //
  307. void CSubclassWndMap::RemoveAll(HWND hwnd)
  308. {
  309. CSubclassWnd* pSubclassWnd;
  310. while ((pSubclassWnd = Lookup(hwnd))!=NULL)
  311. {
  312. if (pSubclassWnd->m_pSubclasser)
  313. pSubclassWnd->m_pSubclasser->ScPreDetachWindow();
  314. else
  315. pSubclassWnd->PreDetachWindow();
  316. pSubclassWnd->HookWindow((HWND)NULL); // (unhook)
  317. if (pSubclassWnd->m_pSubclasser)
  318. pSubclassWnd->m_pSubclasser->ScPostDetachWindow();
  319. else
  320. pSubclassWnd->PostDetachWindow();
  321. }
  322. }
  323. /////////////////
  324. // Find first hook associate with window
  325. //
  326. CSubclassWnd* CSubclassWndMap::Lookup(HWND hwnd)
  327. {
  328. CSubclassWnd* pFound = NULL;
  329. if (!CMapPtrToPtr::Lookup(hwnd, (void*&)pFound))
  330. return NULL;
  331. // ASSERT_KINDOF(CSubclassWnd, pFound);
  332. return pFound;
  333. }