Subclass.cpp
上传用户:wlkj888
上传日期:2022-08-01
资源大小:806k
文件大小:8k
源码类别:

对话框与窗口

开发平台:

Visual C++

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