DumontEXE 0.0.1
Subclass.cpp
00001 ////////////////////////////////////////////////////////////////
00002 // 1997 Microsoft Systems Journal. 
00003 // If this program works, it was written by Paul DiLascia.
00004 // If not, I don't know who wrote it.
00005 //
00006 // CSubclassWnd is a generic class for hooking another window's messages.
00007 
00008 #include "StdAfx.h"
00009 #include "Subclass.h"
00010 
00011 #ifdef _DEBUG
00012 #define new DEBUG_NEW
00013 #undef THIS_FILE
00014 static char THIS_FILE[] = __FILE__;
00015 #endif
00016 
00017 //////////////////
00018 // The message hook map is derived from CMapPtrToPtr, which associates
00019 // a pointer with another pointer. It maps an HWND to a CSubclassWnd, like
00020 // the way MFC's internal maps map HWND's to CWnd's. The first CSubclassWnd
00021 // attached to a window is stored in the map; all other CSubclassWnd's for that
00022 // window are then chained via CSubclassWnd::m_pNext.
00023 //
00024 class CSubclassWndMap : private CMapPtrToPtr 
00025 {
00026 public:
00027   CSubclassWndMap();
00028   ~CSubclassWndMap();
00029   static CSubclassWndMap& GetHookMap();
00030   void Add(HWND hwnd, CSubclassWnd* pSubclassWnd);
00031   void Remove(CSubclassWnd* pSubclassWnd);
00032   void RemoveAll(HWND hwnd);
00033   CSubclassWnd* Lookup(HWND hwnd);
00034 };
00035 
00036 // This trick is used so the hook map isn't
00037 // instantiated until someone actually requests it.
00038 //
00039 #define  theHookMap  (CSubclassWndMap::GetHookMap())
00040 #define  theSafeMap  (CSubclassWnd::GetValidMap())
00041 
00042 ISubclassCallback* CSubclassWnd::s_pCallback = NULL;
00043 
00044 CSubclassWnd::CSubclassWnd()
00045 {
00046   m_pNext = NULL;
00047   m_pOldWndProc = NULL;  
00048   m_hWndHooked  = NULL;
00049   m_pSubclasser = NULL;
00050 }
00051 
00052 CSubclassWnd::~CSubclassWnd()
00053 {
00054   if (m_hWndHooked)
00055     HookWindow((HWND)NULL);
00056 
00057   ASSERT(m_hWndHooked==NULL);    // can't destroy while still hooked!
00058   ASSERT(m_pOldWndProc==NULL);
00059 }
00060 
00061 //////////////////
00062 // Hook a window.
00063 // This installs a new window proc that directs messages to the CSubclassWnd.
00064 // pWnd=NULL to remove.
00065 //
00066 BOOL CSubclassWnd::HookWindow(HWND hWnd, CSubclasser* pSubclasser)
00067 {
00068   if( hWnd )
00069   {
00070     // Hook the window
00071     ASSERT( m_hWndHooked == NULL );
00072 
00073     if( m_hWndHooked ) // only once
00074       return FALSE;
00075 
00076     ASSERT( ::IsWindow(hWnd) );
00077 
00078     if( !::IsWindow(hWnd) )
00079       return FALSE;
00080 
00081     m_hWndHooked = hWnd;
00082 
00083     theHookMap.Add( m_hWndHooked, this );      // Add to map of hooks
00084     theSafeMap.SetAt( (void*)this, NULL );
00085 
00086     m_pSubclasser = pSubclasser;
00087   } 
00088   else 
00089   {
00090     // Unhook the window
00091     if( m_hWndHooked )
00092       theHookMap.Remove( this );        // Remove from map
00093 
00094     theSafeMap.RemoveKey( (void*) this );
00095 
00096     m_pOldWndProc = NULL;
00097     m_pSubclasser = NULL;
00098     m_hWndHooked  = NULL;
00099     m_pNext       = NULL;
00100   }
00101 
00102   return TRUE;
00103 }
00104 
00105 //////////////////
00106 // Window proc-like virtual function which specific CSubclassWnds will
00107 // override to do stuff. Default passes the message to the next hook; 
00108 // the last hook passes the message to the original window.
00109 // You MUST call this at the end of your WindowProc if you want the real
00110 // window to get the message. This is just like CWnd::WindowProc, except that
00111 // a CSubclassWnd is not a window.
00112 //
00113 LRESULT CSubclassWnd::WindowProc(HWND hRealWnd, UINT msg, WPARAM wp, LPARAM lp)
00114 {
00115   if( !::IsWindow(m_hWndHooked) )
00116     return 0;
00117 
00118   ASSERT( m_pOldWndProc );
00119 
00120   if( m_pNext )
00121   {
00122     if( m_pNext->m_pSubclasser )
00123       return m_pNext->m_pSubclasser->ScWindowProc( m_hWndHooked, msg, wp, lp );
00124     else
00125       return m_pNext->WindowProc( m_hWndHooked, msg, wp, lp );
00126   }
00127   else  
00128     return ::CallWindowProc( m_pOldWndProc, m_hWndHooked, msg, wp, lp );
00129 }
00130 
00131 //////////////////
00132 // Like calling base class WindowProc, but with no args, so individual
00133 // message handlers can do the default thing. Like CWnd::Default
00134 //
00135 LRESULT CSubclassWnd::Default()
00136 {
00137   if( !::IsWindow(m_hWndHooked) )
00138     return 0;
00139 
00140   // MFC stores current MSG in thread state
00141   MSG& curMsg = AfxGetThreadState()->m_lastSentMsg;
00142 
00143   // Note: must explicitly call CSubclassWnd::WindowProc to avoid infinite
00144   // recursion on virtual function
00145   return CSubclassWnd::WindowProc( m_hWndHooked, curMsg.message, curMsg.wParam, curMsg.lParam );
00146 }
00147 
00148 //////////////////
00149 // Subclassed window proc for message hooks. Replaces AfxWndProc (or whatever
00150 // else was there before.)
00151 //
00152 LRESULT CALLBACK CSubclassWnd::HookWndProc( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp )
00153 {
00154 #ifdef _USRDLL
00155   // If this is a DLL, need to set up MFC state
00156   AFX_MANAGE_STATE(AfxGetStaticModuleState());
00157 #endif
00158 
00159   // Set up MFC message state just in case anyone wants it
00160   // This is just like AfxCallWindowProc, but we can't use that because
00161   // a CSubclassWnd is not a CWnd.
00162   //
00163   MSG& curMsg = AfxGetThreadState()->m_lastSentMsg;
00164   MSG  oldMsg = curMsg;   // save for nesting
00165   curMsg.hwnd    = hwnd;
00166   curMsg.message = msg;
00167   curMsg.wParam  = wp;
00168   curMsg.lParam  = lp;
00169 
00170   // Get hook object for this window. Get from hook map
00171   CSubclassWnd* pSubclassWnd = theHookMap.Lookup(hwnd);
00172 
00173   if (!pSubclassWnd)
00174     return 0;
00175 
00176   // see if this is a re-entrant call
00177   BOOL bInHookWndProc = FALSE;
00178 
00179   VERIFY( theSafeMap.Lookup((void*)pSubclassWnd, (void*&)bInHookWndProc) );
00180   ASSERT( !bInHookWndProc );
00181 
00182   LRESULT lr;
00183 
00184   if( msg == WM_NCDESTROY )
00185   {
00186 #ifdef _DEBUG
00187     char szClass[30];
00188     GetClassName(hwnd, szClass, 30);
00189     TRACE( "CSubclassWnd::HookWndProc(%s, WM_NCDESTROY)\n", szClass );
00190 #endif
00191 
00192     // Window is being destroyed: unhook all hooks (for this window)
00193     // and pass msg to orginal window proc
00194     WNDPROC wndproc = pSubclassWnd->m_pOldWndProc;
00195     theHookMap.RemoveAll( hwnd );
00196 
00197     lr = ::CallWindowProc( wndproc, hwnd, msg, wp, lp );
00198 
00199     if( s_pCallback )
00200       s_pCallback->PostNcDestroy( hwnd );
00201   } 
00202   else 
00203   {
00204     if( pSubclassWnd->m_pSubclasser )
00205     {
00206       // pass to msg hook
00207       lr = pSubclassWnd->m_pSubclasser->ScWindowProc( pSubclassWnd->GetHwnd(), msg, wp, lp );
00208     }
00209     else
00210     {
00211       // pass to msg hook
00212       lr = pSubclassWnd->WindowProc( pSubclassWnd->GetHwnd(), msg, wp, lp );
00213     }
00214   }
00215 
00216   theSafeMap[(void*)pSubclassWnd] = NULL;
00217 
00218   curMsg = oldMsg;      // pop state
00219   return lr;
00220 }
00221 
00222 BOOL CSubclassWnd::PostMessage( UINT message, WPARAM wParam, LPARAM lParam ) const
00223 {
00224   if( IsValid() )
00225     return ::PostMessage( m_hWndHooked, message, wParam, lParam );
00226 
00227   // else
00228   return FALSE;
00229 }
00230 
00231 BOOL CSubclassWnd::SendMessage( UINT message, WPARAM wParam, LPARAM lParam ) const
00232 {
00233   if( IsValid() )
00234     return ::SendMessage( m_hWndHooked, message, wParam, lParam );
00235 
00236   // else
00237   return FALSE;
00238 }
00239 
00240 CMapPtrToPtr& CSubclassWnd::GetValidMap()
00241 {
00242   // By creating theMap here, C++ doesn't instantiate it until/unless
00243   // it's ever used! This is a good trick to use in C++, to
00244   // instantiate/initialize a static object the first time it's used.
00245   //
00246   static CMapPtrToPtr theMap;
00247   return theMap;
00248 }
00249 
00250 BOOL CSubclassWnd::IsValid( const CSubclassWnd* pScWnd )
00251 {
00252   void* pResult;
00253 
00254   return theSafeMap.Lookup( (void*)pScWnd, pResult );
00255 }
00256 
00257 void CSubclassWnd::ClientToWindow( LPRECT pRect )
00258 {
00259   ::ClientToScreen(        m_hWndHooked,  (LPPOINT)pRect         );
00260   ::ClientToScreen(        m_hWndHooked, ((LPPOINT)pRect) + 1    );
00261   ::MapWindowPoints( NULL, m_hWndHooked,  (LPPOINT)pRect, 1      );
00262   ::MapWindowPoints( NULL, m_hWndHooked, ((LPPOINT)pRect) + 1, 1 );
00263 }
00264 
00265 void CSubclassWnd::ScreenToClient(LPRECT pRect)
00266 {
00267   ::ScreenToClient( m_hWndHooked,  (LPPOINT)pRect      );
00268   ::ScreenToClient( m_hWndHooked, ((LPPOINT)pRect) + 1 );
00269 }
00270 
00271 void CSubclassWnd::ClientToScreen(LPRECT pRect)
00272 {
00273   ::ClientToScreen( m_hWndHooked,  (LPPOINT)pRect      );
00274   ::ClientToScreen( m_hWndHooked, ((LPPOINT)pRect) + 1 );
00275 }
00276 
00277 ////////////////////////////////////////////////////////////////
00278 // CSubclassWndMap implementation
00279 
00280 CSubclassWndMap::CSubclassWndMap()
00281 {
00282 }
00283 
00284 CSubclassWndMap::~CSubclassWndMap()
00285 {
00286 //  ASSERT(IsEmpty());  // all hooks should be removed!  
00287 }
00288 
00289 //////////////////
00290 // Get the one and only global hook map
00291 // 
00292 CSubclassWndMap& CSubclassWndMap::GetHookMap()
00293 {
00294   // By creating theMap here, C++ doesn't instantiate it until/unless
00295   // it's ever used! This is a good trick to use in C++, to
00296   // instantiate/initialize a static object the first time it's used.
00297   //
00298   static CSubclassWndMap theMap;
00299   return theMap;
00300 }
00301 
00302 /////////////////
00303 // Add hook to map; i.e., associate hook with window
00304 //
00305 void CSubclassWndMap::Add(HWND hwnd, CSubclassWnd* pSubclassWnd)
00306 {
00307   ASSERT( hwnd && ::IsWindow(hwnd) );
00308 
00309   // Add to front of list
00310   pSubclassWnd->m_pNext = Lookup( hwnd );
00311   SetAt( hwnd, pSubclassWnd );
00312   
00313   if( pSubclassWnd->m_pNext == NULL )
00314   {
00315     // If this is the first hook added, subclass the window
00316     WNDPROC wndProc = (WNDPROC)GetWindowLong( hwnd, GWL_WNDPROC );
00317 
00318     pSubclassWnd->m_pOldWndProc = 
00319       (WNDPROC)SetWindowLong( hwnd, GWL_WNDPROC, (DWORD)CSubclassWnd::HookWndProc );
00320   } 
00321   else 
00322   {
00323     // just copy wndproc from next hook
00324     pSubclassWnd->m_pOldWndProc = pSubclassWnd->m_pNext->m_pOldWndProc;
00325   }
00326   ASSERT( pSubclassWnd->m_pOldWndProc );
00327 }
00328 
00329 //////////////////
00330 // Remove hook from map
00331 //
00332 void CSubclassWndMap::Remove(CSubclassWnd* pUnHook)
00333 {
00334   HWND hwnd = pUnHook->m_hWndHooked;
00335   ASSERT( hwnd && ::IsWindow(hwnd) );
00336 
00337   CSubclassWnd* pHook = Lookup( hwnd );
00338 //  ASSERT(pHook);
00339 
00340   if( !pHook )
00341     return;
00342 
00343   if( pHook == pUnHook )
00344   {
00345     // hook to remove is the one in the hash table: replace w/next
00346     if( pHook->m_pNext )
00347       SetAt( hwnd, pHook->m_pNext );
00348     else 
00349     {
00350       // This is the last hook for this window: restore wnd proc
00351       RemoveKey( hwnd );
00352       SetWindowLong( hwnd, GWL_WNDPROC, (DWORD)pHook->m_pOldWndProc );
00353     }
00354   } 
00355   else 
00356   {
00357     // Hook to remove is in the middle: just remove from linked list
00358     while( pHook->m_pNext && pHook->m_pNext != pUnHook )
00359       pHook = pHook->m_pNext;
00360 
00361     if( pHook )
00362     {
00363       ASSERT( pHook->m_pNext == pUnHook );
00364       pHook->m_pNext = pUnHook->m_pNext;
00365     }
00366   }
00367 }
00368 
00369 //////////////////
00370 // Remove all the hooks for a window
00371 //
00372 void CSubclassWndMap::RemoveAll( HWND hwnd )
00373 {
00374   CSubclassWnd* pSubclassWnd;
00375 
00376   while( (pSubclassWnd = Lookup( hwnd )) != NULL )
00377   {
00378     if( pSubclassWnd->m_pSubclasser )
00379       pSubclassWnd->m_pSubclasser->ScPreDetachWindow();
00380     else
00381       pSubclassWnd->PreDetachWindow();
00382 
00383     pSubclassWnd->HookWindow( (HWND)NULL );  // (unhook)
00384 
00385     if( pSubclassWnd->m_pSubclasser )
00386       pSubclassWnd->m_pSubclasser->ScPostDetachWindow();
00387     else
00388       pSubclassWnd->PostDetachWindow();
00389   }
00390 }
00391 
00392 /////////////////
00393 // Find first hook associate with window
00394 //
00395 CSubclassWnd* CSubclassWndMap::Lookup( HWND hwnd )
00396 {
00397   CSubclassWnd* pFound = NULL;
00398 
00399   if( !CMapPtrToPtr::Lookup( hwnd, (void*&)pFound ) )
00400     return NULL;
00401 
00402 //  ASSERT_KINDOF(CSubclassWnd, pFound);
00403 
00404   return pFound;
00405 }
 All Classes Namespaces Functions Variables Enumerations Enumerator Properties




~ ~ ~ ~ ~ ~
Source Code without Comments is like a Cranberry Garland
without the berries. Comment your Code!
 
Commence Database User Support Group Forum
http://newsgroup.showoff-db.org/
~ ~ ~ ~ ~ ~
Author: Mark Petryk
Lorimark Solutions, LLC
mark@lorimarksolutions.com