Subclass.cpp
上传用户:vipseo
上传日期:2010-02-15
资源大小:137k
文件大小:8k
源码类别:

组合框控件

开发平台:

Visual C++

  1. ////////////////////////////////////////////////////////////////
  2. // Copyright 1998 Paul DiLascia
  3. // If this code 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. //
  8. /////////////////////////////////////////////////////////////////////////////
  9. /****************************************************************************
  10.  *
  11.  * $Date: 10/14/99 12:41p $
  12.  * $Revision: 2 $
  13.  * $Archive: /CodeJock/CJLibrary/Subclass.cpp $
  14.  *
  15.  * $History: Subclass.cpp $
  16.  * 
  17.  * *****************  Version 2  *****************
  18.  * User: Kirk Stowell Date: 10/14/99   Time: 12:41p
  19.  * Updated in $/CodeJock/CJLibrary
  20.  * Added source control history to file header.
  21.  *
  22.  ***************************************************************************/
  23. /////////////////////////////////////////////////////////////////////////////
  24. #include "StdAfx.h"
  25. #include "Subclass.h"
  26. #ifdef _DEBUG
  27. #define new DEBUG_NEW
  28. #undef THIS_FILE
  29. static char THIS_FILE[] = __FILE__;
  30. #endif
  31. //////////////////
  32. // The message hook map is derived from CMapPtrToPtr, which associates
  33. // a pointer with another pointer. It maps an HWND to a CSubclassWnd, like
  34. // the way MFC's internal maps map HWND's to CWnd's. The first CSubclassWnd
  35. // attached to a window is stored in the map; all other CSubclassWnd's for that
  36. // window are then chained via CSubclassWnd::m_pNext.
  37. //
  38. class CSubclassWndMap : private CMapPtrToPtr {
  39. public:
  40. CSubclassWndMap();
  41. ~CSubclassWndMap();
  42. static CSubclassWndMap& GetHookMap();
  43. void Add(HWND hwnd, CSubclassWnd* pSubclassWnd);
  44. void Remove(CSubclassWnd* pSubclassWnd);
  45. void RemoveAll(HWND hwnd);
  46. CSubclassWnd* Lookup(HWND hwnd);
  47. };
  48. // This trick is used so the hook map isn't
  49. // instantiated until someone actually requests it.
  50. //
  51. #define theHookMap (CSubclassWndMap::GetHookMap())
  52. IMPLEMENT_DYNAMIC(CSubclassWnd, CWnd);
  53. CSubclassWnd::CSubclassWnd()
  54. {
  55. m_pNext = NULL;
  56. m_pOldWndProc = NULL;
  57. m_hWnd  = NULL;
  58. }
  59. CSubclassWnd::~CSubclassWnd()
  60. {
  61. if (m_hWnd) 
  62. HookWindow((HWND)NULL); // unhook window
  63. }
  64. //////////////////
  65. // Hook a window.
  66. // This installs a new window proc that directs messages to the CSubclassWnd.
  67. // pWnd=NULL to remove.
  68. //
  69. BOOL CSubclassWnd::HookWindow(HWND hwnd)
  70. {
  71. ASSERT_VALID(this);
  72. if (hwnd) {
  73. // Hook the window
  74. ASSERT(m_hWnd==NULL);
  75. ASSERT(::IsWindow(hwnd));
  76. theHookMap.Add(hwnd, this); // Add to map of hooks
  77. } else if (m_hWnd) {
  78. // Unhook the window
  79. theHookMap.Remove(this); // Remove from map
  80. m_pOldWndProc = NULL;
  81. }
  82. m_hWnd = hwnd;
  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(UINT msg, WPARAM wp, LPARAM lp)
  94. {
  95. // ASSERT_VALID(this);  // removed for speed
  96. ASSERT(m_pOldWndProc);
  97. return m_pNext ? m_pNext->WindowProc(msg, wp, lp) :
  98. ::CallWindowProc(m_pOldWndProc, m_hWnd, msg, wp, lp);
  99. }
  100. //////////////////
  101. // Like calling base class WindowProc, but with no args, so individual
  102. // message handlers can do the default thing. Like CWnd::Default
  103. //
  104. LRESULT CSubclassWnd::Default()
  105. {
  106. // MFC stores current MSG in thread state
  107. MSG& curMsg = AfxGetThreadState()->m_lastSentMsg;
  108. // Note: must explicitly call CSubclassWnd::WindowProc to avoid infinte
  109. // recursion on virtual function
  110. return CSubclassWnd::WindowProc(curMsg.message, curMsg.wParam, curMsg.lParam);
  111. }
  112. #ifdef _DEBUG
  113. void CSubclassWnd::AssertValid() const
  114. {
  115. CObject::AssertValid();
  116. ASSERT(m_hWnd==NULL || ::IsWindow(m_hWnd));
  117. if (m_hWnd) {
  118. for (CSubclassWnd* p = theHookMap.Lookup(m_hWnd); p; p=p->m_pNext) {
  119. if (p==this)
  120. break;
  121. }
  122. ASSERT(p); // should have found it!
  123. }
  124. }
  125. void CSubclassWnd::Dump(CDumpContext& dc) const
  126. {
  127. CObject::Dump(dc);
  128. }
  129. #endif
  130. //////////////////
  131. // Subclassed window proc for message hooks. Replaces AfxWndProc (or whatever
  132. // else was there before.)
  133. //
  134. LRESULT CALLBACK
  135. HookWndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
  136. {
  137. #ifdef _USRDLL
  138. // If this is a DLL, need to set up MFC state
  139. AFX_MANAGE_STATE(AfxGetStaticModuleState());
  140. #endif
  141. // Set up MFC message state just in case anyone wants it
  142. // This is just like AfxCallWindowProc, but we can't use that because
  143. // a CSubclassWnd is not a CWnd.
  144. //
  145. MSG& curMsg = AfxGetThreadState()->m_lastSentMsg;
  146. MSG  oldMsg = curMsg;   // save for nesting
  147. curMsg.hwnd = hwnd;
  148. curMsg.message = msg;
  149. curMsg.wParam  = wp;
  150. curMsg.lParam  = lp;
  151. // Get hook object for this window. Get from hook map
  152. CSubclassWnd* pSubclassWnd = theHookMap.Lookup(hwnd);
  153. ASSERT(pSubclassWnd);
  154. LRESULT lr;
  155. if (msg==WM_NCDESTROY) {
  156. // Window is being destroyed: unhook all hooks (for this window)
  157. // and pass msg to orginal window proc
  158. //
  159. WNDPROC wndproc = pSubclassWnd->m_pOldWndProc;
  160. theHookMap.RemoveAll(hwnd);
  161. lr = ::CallWindowProc(wndproc, hwnd, msg, wp, lp);
  162. } else {
  163. // pass to msg hook
  164. lr = pSubclassWnd->WindowProc(msg, wp, lp);
  165. }
  166. curMsg = oldMsg; // pop state
  167. return lr;
  168. }
  169. ////////////////////////////////////////////////////////////////
  170. // CSubclassWndMap implementation
  171. //
  172. CSubclassWndMap::CSubclassWndMap()
  173. {
  174. }
  175. CSubclassWndMap::~CSubclassWndMap()
  176. {
  177. // This assert bombs when posting WM_QUIT, so I've deleted it.
  178. // ASSERT(IsEmpty()); // all hooks should be removed!
  179. }
  180. //////////////////
  181. // Get the one and only global hook map
  182. // 
  183. CSubclassWndMap& CSubclassWndMap::GetHookMap()
  184. {
  185. // By creating theMap here, C++ doesn't instantiate it until/unless
  186. // it's ever used! This is a good trick to use in C++, to
  187. // instantiate/initialize a static object the first time it's used.
  188. //
  189. static CSubclassWndMap theMap;
  190. return theMap;
  191. }
  192. /////////////////
  193. // Add hook to map; i.e., associate hook with window
  194. //
  195. void CSubclassWndMap::Add(HWND hwnd, CSubclassWnd* pSubclassWnd)
  196. {
  197. ASSERT(hwnd && ::IsWindow(hwnd));
  198. // Add to front of list
  199. pSubclassWnd->m_pNext = Lookup(hwnd);
  200. SetAt(hwnd, pSubclassWnd);
  201. if (pSubclassWnd->m_pNext==NULL) {
  202. // If this is the first hook added, subclass the window
  203. pSubclassWnd->m_pOldWndProc = 
  204. (WNDPROC)SetWindowLong(hwnd, GWL_WNDPROC, (DWORD)HookWndProc);
  205. } else {
  206. // just copy wndproc from next hook
  207. pSubclassWnd->m_pOldWndProc = pSubclassWnd->m_pNext->m_pOldWndProc;
  208. }
  209. ASSERT(pSubclassWnd->m_pOldWndProc);
  210. }
  211. //////////////////
  212. // Remove hook from map
  213. //
  214. void CSubclassWndMap::Remove(CSubclassWnd* pUnHook)
  215. {
  216. HWND hwnd = pUnHook->m_hWnd;
  217. ASSERT(hwnd && ::IsWindow(hwnd));
  218. CSubclassWnd* pHook = Lookup(hwnd);
  219. ASSERT(pHook);
  220. if (pHook==pUnHook) {
  221. // hook to remove is the one in the hash table: replace w/next
  222. if (pHook->m_pNext)
  223. SetAt(hwnd, pHook->m_pNext);
  224. else {
  225. // This is the last hook for this window: restore wnd proc
  226. RemoveKey(hwnd);
  227. SetWindowLong(hwnd, GWL_WNDPROC, (DWORD)pHook->m_pOldWndProc);
  228. }
  229. } else {
  230. // Hook to remove is in the middle: just remove from linked list
  231. while (pHook->m_pNext!=pUnHook)
  232. pHook = pHook->m_pNext;
  233. ASSERT(pHook && pHook->m_pNext==pUnHook);
  234. pHook->m_pNext = pUnHook->m_pNext;
  235. }
  236. }
  237. //////////////////
  238. // Remove all the hooks for a window
  239. //
  240. void CSubclassWndMap::RemoveAll(HWND hwnd)
  241. {
  242. CSubclassWnd* pSubclassWnd;
  243. while ((pSubclassWnd = Lookup(hwnd))!=NULL)
  244. pSubclassWnd->HookWindow((HWND)NULL); // (unhook)
  245. }
  246. /////////////////
  247. // Find first hook associate with window
  248. //
  249. CSubclassWnd* CSubclassWndMap::Lookup(HWND hwnd)
  250. {
  251. CSubclassWnd* pFound = NULL;
  252. if (!CMapPtrToPtr::Lookup(hwnd, (void*&)pFound))
  253. return NULL;
  254. ASSERT_KINDOF(CSubclassWnd, pFound);
  255. return pFound;
  256. }