subclass.c
Upload User: xhy777
Upload Date: 2007-02-14
Package Size: 24088k
Code Size: 50k
Category:

Windows Kernel

Development Platform:

Visual C++

  1. #include "ctlspriv.h"
  2. ///////////////////////////////////////////////////////////////////////////////
  3. // SUBCLASS.C -- subclassing helper functions
  4. //
  5. //      SetWindowSubclass
  6. //      GetWindowSubclass
  7. //      RemoveWindowSubclass
  8. //      DefSubclassProc
  9. //
  10. //  This module defines helper functions that make subclassing windows safe(er)
  11. // and easy(er).  The code maintains a single property on the subclassed window
  12. // and dispatches various "subclass callbacks" to its clients a required.  The
  13. // client is provided reference data and a simple "default processing" API.
  14. //
  15. // Semantics:
  16. //  A "subclass callback" is identified by a unique pairing of a callback
  17. // function pointer and an unsigned ID value.  Each callback can also store a
  18. // single DWORD of reference data, which is passed to the callback function
  19. // when it is called to filter messages.  No reference counting is performed
  20. // for the callback, it may repeatedly call the SetWindowSubclass API to alter
  21. // the value of its reference data element as desired.
  22. //
  23. // Warning: You cannot use these to subclass a window across threads since
  24. //          the critical sections have been removed. 05-May-97
  25. //
  26. // History:
  27. //  26-April-96  francish        Created.
  28. //  05-May  -97  davidds         Stopped serializing the world.
  29. ///////////////////////////////////////////////////////////////////////////////
  30. //
  31. // NOTE: Although a linked list would have made the code slightly simpler, this
  32. // module uses a packed callback array to avoid unneccessary fragmentation.  fh
  33. //
  34. struct _SUBCLASS_HEADER;
  35. typedef struct
  36. {
  37.     SUBCLASSPROC    pfnSubclass;        // subclass procedure
  38.     WPARAM          uIdSubclass;        // unique subclass identifier
  39.     DWORD_PTR        dwRefData;          // optional ref data
  40. } SUBCLASS_CALL;
  41. typedef struct _SUBCLASS_FRAME
  42. {
  43.     UINT uCallIndex;                    // index of next callback to call
  44.     UINT uDeepestCall;                  // deepest uCallIndex on stack
  45.     struct _SUBCLASS_FRAME *pFramePrev; // previous subclass frame pointer
  46.     struct _SUBCLASS_HEADER *pHeader;   // header associated with this frame
  47. } SUBCLASS_FRAME;
  48. typedef struct _SUBCLASS_HEADER
  49. {
  50.     UINT uRefs;                         // subclass count
  51.     UINT uAlloc;                        // allocated subclass call nodes
  52.     UINT uCleanup;                      // index of call node to clean up
  53.     DWORD dwThreadId;                   // thread id of window we are hooking
  54.     SUBCLASS_FRAME *pFrameCur;          // current subclass frame pointer
  55.     SUBCLASS_CALL CallArray[1];         // base of packed call node array
  56. } SUBCLASS_HEADER;
  57. #define CALLBACK_ALLOC_GRAIN (3)        // 1 defproc, 1 subclass, 1 spare
  58. #ifdef DEBUG
  59. BOOL IsValidPSUBCLASS_CALL(SUBCLASS_CALL * pcall)
  60. {
  61.     return (IS_VALID_WRITE_PTR(pcall, SUBCLASS_CALL) &&
  62.             (NULL == pcall->pfnSubclass || IS_VALID_CODE_PTR(pcall->pfnSubclass, SUBCLASSPROC)));
  63. }   
  64. BOOL IsValidPSUBCLASS_FRAME(SUBCLASS_FRAME * pframe)
  65. {
  66.     return (IS_VALID_WRITE_PTR(pframe, SUBCLASS_FRAME) && 
  67.             IS_VALID_WRITE_PTR(pframe->pHeader, SUBCLASS_HEADER) &&
  68.             (NULL == pframe->pFramePrev || IS_VALID_WRITE_PTR(pframe->pFramePrev, SUBCLASS_FRAME)));
  69. }    
  70.  
  71. BOOL IsValidPSUBCLASS_HEADER(SUBCLASS_HEADER * phdr)
  72. {
  73.     BOOL bRet = (IS_VALID_WRITE_PTR(phdr, SUBCLASS_HEADER) &&
  74.                  (NULL == phdr->pFrameCur || IS_VALID_STRUCT_PTR(phdr->pFrameCur, SUBCLASS_FRAME)) &&
  75.                  IS_VALID_WRITE_BUFFER(phdr->CallArray, SUBCLASS_CALL, phdr->uAlloc));
  76.     if (bRet)
  77.     {
  78.         UINT i;
  79.         SUBCLASS_CALL * pcall = phdr->CallArray;
  80.         for (i = 0; i < phdr->uRefs; i++, pcall++)
  81.         {
  82.             if (!IS_VALID_STRUCT_PTR(pcall, SUBCLASS_CALL))
  83.                 return FALSE;
  84.         }
  85.     }
  86.     return bRet;
  87. }    
  88. #endif
  89. ///////////////////////////////////////////////////////////////////////////////
  90. // DEBUG CODE TO CHECK IF WINDOW IS ON SAME THREAD AS CALLER
  91. // Since we don't do any serialization, we need this to make sure of this.
  92. ///////////////////////////////////////////////////////////////////////////////
  93. #ifdef DEBUG
  94. BOOL IsWindowOnCurrentThread(HWND hWnd)
  95. {
  96.     DWORD foo;
  97.     if (!IsWindow(hWnd))
  98.         // bail if the window is dead so we dont bogusly rip
  99.         return(TRUE);
  100.     
  101.     if (GetCurrentThreadId() != GetWindowThreadProcessId(hWnd, &foo))
  102.     {
  103.         DebugMsg(TF_ALWAYS, TEXT("wn: WindowSubclass - Called from wrong thread %08X"), hWnd);
  104.         return(FALSE);
  105.     }
  106.     else
  107.         return(TRUE);
  108.               
  109. }
  110. #endif
  111. ///////////////////////////////////////////////////////////////////////////////
  112. LRESULT CALLBACK MasterSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam,
  113.     LPARAM lParam);
  114. LRESULT CallNextSubclassProc(SUBCLASS_HEADER *pHeader, HWND hWnd, UINT uMsg,
  115.     WPARAM wParam, LPARAM lParam);
  116. //-----------------------------------------------------------------------------
  117. // RETAIL_ZOMBIE_MESSAGE_WNDPROC
  118. //
  119. // this macro controls the generation of diagnostic code for an error condition
  120. // in the subclass code (see the SubclassDeath function below).
  121. //
  122. // commenting out this macro will zombie windows using DefWindowProc instead.
  123. //
  124. //-----------------------------------------------------------------------------
  125. //#define RETAIL_ZOMBIE_MESSAGE_WNDPROC
  126. #if defined(RETAIL_ZOMBIE_MESSAGE_WNDPROC) || defined(DEBUG)
  127. #ifndef DEBUG
  128. #pragma message("rnWARNING: disable retail ZombieWndProc before final releasern")
  129. #endif
  130. LRESULT ZombieWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
  131. #else
  132. #define ZombieWndProc DefWindowProc
  133. #endif
  134. //-----------------------------------------------------------------------------
  135. // SubclassDeath
  136. //
  137. // this function is called if we ever enter one of our subclassing procedures
  138. // without our reference data (and hence without the previous wndproc).
  139. //
  140. // hitting this represents a catastrophic failure in the subclass code.
  141. //
  142. // the function resets the wndproc of the window to a 'zombie' window
  143. // procedure to avoid faulting.  the RETAIL_ZOMBIE_MESSAGE_WNDPROC macro above
  144. // controls the generation of diagnostic code for this wndproc.
  145. //
  146. //-----------------------------------------------------------------------------
  147. LRESULT SubclassDeath(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  148. {
  149.     //
  150.     // WE SHOULD NEVER EVER GET HERE
  151.     // if we do please find francish to debug it immediately
  152.     //
  153.     DebugMsg(TF_ALWAYS, TEXT("fatal: SubclassDeath in window %08X"), hWnd);
  154. #ifdef DEBUG    
  155.     //
  156.     // if we are in a debugger, stop now regardless of break flags
  157.     //
  158.     __try { DebugBreak(); } __except(EXCEPTION_EXECUTE_HANDLER) {;} __endexcept
  159. #endif
  160.     
  161.     //
  162.     // we call the outside world so prepare to deadlock if we have the critsec
  163.     //
  164. #ifdef FREETHREADEDSUBCLASSGOOP
  165.     ASSERTNONCRITICAL
  166. #endif
  167.     //
  168.     // in theory we could save the original wndproc in a separate property
  169.     // but that just wastes memory for something that should never happen
  170.     //
  171.     // convert this window to a zombie in hopes that it will get debugged
  172.     //
  173.     InvalidateRect(hWnd, NULL, TRUE);
  174.     SubclassWindow(hWnd, ZombieWndProc);
  175.     return ZombieWndProc(hWnd, uMsg, wParam, lParam);
  176. }
  177. //-----------------------------------------------------------------------------
  178. // GetWindowProc
  179. //
  180. // this inline function returns the current wndproc for the specified window.
  181. //
  182. //-----------------------------------------------------------------------------
  183. __inline WNDPROC GetWindowProc(HWND hWnd)
  184. {
  185.     return (WNDPROC)GetWindowLongPtr(hWnd, GWLP_WNDPROC);
  186. }
  187. //-----------------------------------------------------------------------------
  188. // g_aCC32Subclass
  189. //
  190. // This is the global ATOM we use to store our SUBCLASS_HEADER property on
  191. // random windows that come our way.
  192. //
  193. //  HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK
  194. //
  195. //  Win95's property code is BROKEN.  If you SetProp using a text string, USER
  196. // adds and removes atoms for the property symmetrically, including when the
  197. // window is destroyed with properties lying around (good).  Unfortunately, if
  198. // you SetProp using a global atom, USER doesn't do things quite right in the
  199. // window cleanup case.  It uses the atom without adding references in SetProp
  200. // calls and without deleting them in RemoveProp calls (good so far).  However,
  201. // when a window with one of these properties lying around is cleaned up, USER
  202. // will delete the atom on you.  This tends to break apps that do the
  203. // following:
  204. //
  205. //  - MyAtom = GlobalAddAtom("foo");            // at app startup
  206. //  - SetProp(SomeWindow, MyAtom, MyData);
  207. //  - <window gets destroyed, USER deletes atom>
  208. //  - <time passes>
  209. //  - SetProp(SomeOtherWindow, MyAtom, MyData); // fails or uses random atom
  210. //  - GlobalDeleteAtom(MyAtom);                 // fails or deletes random atom
  211. //
  212. //  One might be tempted to ask why this file uses atom properties if they are
  213. // so broken.  Put simply, it is the only way to defend yourself against other
  214. // apps that use atom properties (like the one described above).  Imagine that
  215. // we call SetProp(OurWindow, "bar", OurData) in some other app at about the
  216. // <time passes> point in the sequence above.  USER has just nuked some poor
  217. // app's atom, and we wander into SetProp, which calls GlobalAddAtom, which
  218. // just happens to give us the free slot created by USER's window cleanup code.
  219. // Now we have a real problem because the very same atom is sitting in some
  220. // global variable in the other app, just waiting to be deleted when that app
  221. // exits (Peachtree Accounting tends to be very good at this...)  Of course the
  222. // ultimate outcome of this is that we will call GetProp in some critical
  223. // routine and our data will have vanished (it's actually still in the window's
  224. // property table but GetProp("bar") calls GlobalFindAtom("bar") to get the
  225. // atom to scan the property table for; and that call will fail so the property
  226. // will be missed and we'll get back NULL).
  227. //
  228. //  Basically, we create an atom and aggressively increment its reference count
  229. // so that it can withstand a few GlobalDeleteAtom calls every now and then.
  230. // Since we are using an atom property, we need to worry about USER's cleanup
  231. // code nuking us too.  Thus we just keep incrementing the reference count
  232. // until it pegs.
  233. //
  234. // IEUNIX 
  235. // We doesn't have the above problems, but MainWin SetProp implementation
  236. // doesn't create GlobalAtom when it gets 2nd argument as a string.
  237. // And it doesn't have to - that's non-documented NT/Win95 implementation.
  238. // So, if UNIX, we will use the ATOM in all the cases, marking #ifdef MAINWIN.
  239. //
  240. //  HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK
  241. //
  242. //-----------------------------------------------------------------------------
  243. extern ATOM g_aCC32Subclass;
  244. //-----------------------------------------------------------------------------
  245. // FastGetSubclassHeader
  246. //
  247. // this inline function returns the subclass header for the specified window.
  248. // if the window has no subclass header the return value is NULL.
  249. //
  250. //-----------------------------------------------------------------------------
  251. __inline SUBCLASS_HEADER *FastGetSubclassHeader(HWND hWnd)
  252. {
  253.     return  (g_aCC32Subclass ?
  254.             ((SUBCLASS_HEADER *)GetProp(hWnd, MAKEINTATOM(g_aCC32Subclass))) :
  255.             NULL);
  256. }
  257. //-----------------------------------------------------------------------------
  258. // GetSubclassHeader
  259. //
  260. // this function returns the subclass header for the specified window.  it
  261. // fails if the caller is on the wrong process, but will allow the caller to
  262. // get the header from a thread other than the specified window's thread.
  263. //
  264. //-----------------------------------------------------------------------------
  265. SUBCLASS_HEADER *GetSubclassHeader(HWND hWnd)
  266. {
  267.     DWORD dwProcessId;
  268.     //
  269.     // only return the header if we are in the right process
  270.     //
  271.     if (!GetWindowThreadProcessId(hWnd, &dwProcessId))
  272.         dwProcessId = 0;
  273.     if (dwProcessId != GetCurrentProcessId())
  274.     {
  275.         if (dwProcessId)
  276.             DebugMsg(TF_ALWAYS, TEXT("error: XxxWindowSubclass - wrong process for window %08X"), hWnd);
  277.         ASSERT(FALSE);
  278.         return NULL;
  279.     }
  280.     //
  281.     // return the header
  282.     //
  283.     return FastGetSubclassHeader(hWnd);
  284. }
  285. //-----------------------------------------------------------------------------
  286. // SetSubclassHeader
  287. //
  288. // this function sets the subclass header for the specified window.
  289. //
  290. //-----------------------------------------------------------------------------
  291. BOOL SetSubclassHeader(HWND hWnd, SUBCLASS_HEADER *pHeader,
  292.     SUBCLASS_FRAME *pFrameFixup)
  293. {
  294.     ATOM a;
  295.     BOOL fResult = TRUE;    // assume success
  296.     ASSERT(NULL == pHeader || IS_VALID_STRUCT_PTR(pHeader, SUBCLASS_HEADER));
  297.     ASSERT(NULL == pFrameFixup || IS_VALID_STRUCT_PTR(pFrameFixup, SUBCLASS_FRAME));
  298. #ifdef FREETHREADEDSUBCLASSGOOP
  299.     ASSERTCRITICAL;         // we are partying on the header and frame list
  300. #else
  301.     ASSERT(IsWindowOnCurrentThread(hWnd));
  302. #endif
  303.     if (g_aCC32Subclass == 0) {
  304.         //
  305.         // HACK: we are intentionally incrementing the refcount on this atom
  306.         // WE DO NOT WANT IT TO GO BACK DOWN so we will not delete it in process
  307.         // detach (see comments for g_aCC32Subclass in subclass.c for more info)
  308.         //
  309.         if ((a = GlobalAddAtom(c_szCC32Subclass)) != 0)
  310.             g_aCC32Subclass = a;    // in case the old atom got nuked
  311.     }
  312.     //
  313.     // update the frame list if required
  314.     //
  315.     while (pFrameFixup)
  316.     {
  317.         pFrameFixup->pHeader = pHeader;
  318.         pFrameFixup = pFrameFixup->pFramePrev;
  319.     }
  320.     //
  321.     // do we have a window to update?
  322.     //
  323.     if (hWnd)
  324.     {
  325.         //
  326.         // update/remove the property as required
  327.         //
  328.         if (!pHeader)
  329.         {
  330.             //
  331.             // HACK: we remove with an ATOM so the refcount won't drop
  332.             //          (see comments for g_aCC32Subclass above)
  333.             //
  334.             RemoveProp(hWnd, MAKEINTATOM(g_aCC32Subclass));
  335.         }
  336.         else
  337.         {
  338.             LPCTSTR lpPropAtomOrStr;
  339. #ifndef MAINWIN
  340.             //
  341.             // HACK: we add using a STRING so the refcount will go up
  342.             //          (see comments for g_aCC32Subclass above)
  343.             //
  344.             lpPropAtomOrStr = c_szCC32Subclass;
  345. #else
  346.             if (! g_aCC32Subclass) 
  347.                 g_aCC32Subclass = GlobalAddAtom(c_szCC32Subclass);
  348.         
  349.             if (! g_aCC32Subclass) {
  350.                 DebugMsg(TF_ALWAYS, TEXT("wn: SetWindowSubclass - couldn't subclass window %08X
  351.                          GlobalAddAtom failed for %s"), hWnd, c_szCC32Subclass);
  352.                 return FALSE;
  353.             }
  354.             lpPropAtomOrStr = g_aCC32Subclass;
  355. #endif
  356.             if (!SetProp(hWnd, lpPropAtomOrStr, (HANDLE)pHeader))
  357.             {
  358.                 DebugMsg(TF_ALWAYS, TEXT("wn: SetWindowSubclass - couldn't subclass window %08X"), hWnd);
  359.                 fResult = FALSE;
  360.             }
  361.         }
  362.     }
  363.     return fResult;
  364. }
  365. //-----------------------------------------------------------------------------
  366. // FreeSubclassHeader
  367. //
  368. // this function frees the subclass header for the specified window.
  369. //
  370. //-----------------------------------------------------------------------------
  371. void FreeSubclassHeader(HWND hWnd, SUBCLASS_HEADER *pHeader)
  372. {
  373. #ifdef FREETHREADEDSUBCLASSGOOP
  374.     ASSERTCRITICAL;                 // we will be removing the subclass header
  375. #else
  376.     ASSERT(IsWindowOnCurrentThread(hWnd));    
  377. #endif
  378.     //
  379.     // sanity
  380.     //
  381.     if (!pHeader)
  382.     {
  383.         ASSERT(FALSE);
  384.         return;
  385.     }
  386.     //
  387.     // clean up the header
  388.     //
  389.     SetSubclassHeader(hWnd, NULL, pHeader->pFrameCur);
  390.     LocalFree((HANDLE)pHeader);
  391. }
  392. //-----------------------------------------------------------------------------
  393. // ReAllocSubclassHeader
  394. //
  395. // this function allocates/reallocates a subclass header for the specified
  396. // window.
  397. //
  398. //-----------------------------------------------------------------------------
  399. SUBCLASS_HEADER *ReAllocSubclassHeader(HWND hWnd, SUBCLASS_HEADER *pHeader,
  400.     UINT uCallbacks)
  401. {
  402.     UINT uAlloc;
  403.     ASSERT(NULL == pHeader || IS_VALID_STRUCT_PTR(pHeader, SUBCLASS_HEADER));
  404. #ifdef FREETHREADEDSUBCLASSGOOP
  405.     ASSERTCRITICAL;     // we will be replacing the subclass header
  406. #else
  407.     ASSERT(IsWindowOnCurrentThread(hWnd));    
  408. #endif
  409.     //
  410.     // granularize the allocation
  411.     //
  412.     uAlloc = CALLBACK_ALLOC_GRAIN *
  413.         ((uCallbacks + CALLBACK_ALLOC_GRAIN - 1) / CALLBACK_ALLOC_GRAIN);
  414.     //
  415.     // do we need to change the allocation?
  416.     //
  417.     if (!pHeader || (uAlloc != pHeader->uAlloc))
  418.     {
  419.         //
  420.         // compute bytes required
  421.         //
  422.         uCallbacks = uAlloc * sizeof(SUBCLASS_CALL) + sizeof(SUBCLASS_HEADER);
  423.         //
  424.         // and try to alloc
  425.         //
  426.         pHeader = CCLocalReAlloc(pHeader, uCallbacks);
  427.         //
  428.         // did it work?
  429.         //
  430.         if (pHeader)
  431.         {
  432.             //
  433.             // yup, update info
  434.             //
  435.             pHeader->uAlloc = uAlloc;
  436.             if (!SetSubclassHeader(hWnd, pHeader, pHeader->pFrameCur))
  437.             {
  438.                 FreeSubclassHeader(hWnd, pHeader);
  439.                 pHeader = NULL;
  440.             }
  441.         }
  442.     }
  443.     ASSERT(pHeader);
  444.     return pHeader;
  445. }
  446. //-----------------------------------------------------------------------------
  447. // CallOriginalWndProc
  448. //
  449. // this procedure is the default SUBCLASSPROC which is always installed when we
  450. // subclass a window.  the original window procedure is installed as the
  451. // reference data for this callback.  it simply calls the original wndproc and
  452. // returns its result.
  453. //
  454. //-----------------------------------------------------------------------------
  455. LRESULT CALLBACK CallOriginalWndProc(HWND hWnd, UINT uMsg, WPARAM wParam,
  456.     LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
  457. {
  458.     //
  459.     // dwRefData should be the original window procedure
  460.     //
  461.     ASSERT(dwRefData);
  462.     //
  463.     // and call it
  464.     //
  465.     return CallWindowProc((WNDPROC)dwRefData, hWnd, uMsg, wParam, lParam);
  466. }
  467. //-----------------------------------------------------------------------------
  468. // AttachSubclassHeader
  469. //
  470. // this procedure makes sure that a given window is subclassed by us.  it
  471. // maintains a reference count on the data structures associated with our
  472. // subclass.  if the window is not yet subclassed by us then this procedure
  473. // installs our subclass procedure and associated data structures.
  474. //
  475. //-----------------------------------------------------------------------------
  476. SUBCLASS_HEADER *AttachSubclassHeader(HWND hWnd)
  477. {
  478.     SUBCLASS_HEADER *pHeader;
  479.     DWORD dwThreadId;
  480.     //
  481.     // we party on the subclass call chain here
  482.     //
  483. #ifdef FREETHREADEDSUBCLASSGOOP
  484.     ASSERTCRITICAL;
  485. #else
  486.     ASSERT(IsWindowOnCurrentThread(hWnd));    
  487. #endif
  488.     //
  489.     // we only call SetWindowLong for the first caller, which would cause this
  490.     // operation to work out of context sometimes and fail others...
  491.     // artifically prevent people from subclassing from the wrong thread
  492.     //  
  493.     if ((dwThreadId = GetWindowThreadProcessId(hWnd, NULL)) !=
  494.         GetCurrentThreadId())
  495.     {
  496.         AssertMsg(FALSE, TEXT("error: SetWindowSubclass - wrong thread for window %08X"), hWnd);
  497.         return NULL;
  498.     }
  499.     //
  500.     // if haven't already subclassed the window then do it now
  501.     //
  502.     if ((pHeader = GetSubclassHeader(hWnd)) == NULL)
  503.     {
  504.         WNDPROC pfnOldWndProc;
  505.         SUBCLASS_CALL *pCall;
  506.         //
  507.         // attach our header data to the window
  508.         // we need space for two callbacks; the subclass and the original proc
  509.         //
  510.         if ((pHeader = ReAllocSubclassHeader(hWnd, NULL, 2)) == NULL)
  511.             return NULL;
  512.         pHeader->dwThreadId = dwThreadId;
  513.         //
  514.         // actually subclass the window
  515.         //
  516.         if ((pfnOldWndProc = SubclassWindow(hWnd, MasterSubclassProc)) == NULL)
  517.         {
  518.             // clean up and get out
  519.             FreeSubclassHeader(hWnd, pHeader);
  520.             return NULL;
  521.         }
  522.         //
  523.         // set up the first node in the array to call the original wndproc
  524.         //
  525.         ASSERT(pHeader->uAlloc);
  526.         pCall = pHeader->CallArray;
  527.         pCall->pfnSubclass = CallOriginalWndProc;
  528.         pCall->uIdSubclass = 0;
  529.         pCall->dwRefData   = (DWORD_PTR)pfnOldWndProc;
  530.         //
  531.         // init our subclass refcount...
  532.         //
  533.         pHeader->uRefs = 1;
  534.     }
  535.     return pHeader;
  536. }
  537. //-----------------------------------------------------------------------------
  538. // DetachSubclassHeader
  539. //
  540. // this procedure attempts to detach the subclass header from the specified
  541. // window
  542. //
  543. //-----------------------------------------------------------------------------
  544. void DetachSubclassHeader(HWND hWnd, SUBCLASS_HEADER *pHeader, BOOL fForce)
  545. {
  546.     WNDPROC pfnOldWndProc;
  547. #ifdef DEBUG
  548.     SUBCLASS_CALL *pCall;
  549.     UINT uCur;
  550. #endif
  551. #ifdef FREETHREADEDSUBCLASSGOOP
  552.     ASSERTCRITICAL;         // we party on the subclass call chain here
  553. #else
  554.     ASSERT(IsWindowOnCurrentThread(hWnd));    
  555. #endif
  556.     ASSERT(pHeader);        // fear
  557.     //
  558.     // if we are not being forced to remove and the window is still valid then
  559.     // sniff around a little and decide if it's a good idea to detach now
  560.     //
  561.     if (!fForce && hWnd)
  562.     {
  563.         ASSERT(pHeader == FastGetSubclassHeader(hWnd)); // paranoia
  564.         //
  565.         // do we still have active clients?
  566.         //
  567.         if (pHeader->uRefs > 1)
  568.             return;
  569.         ASSERT(pHeader->uRefs); // should always have the "call original" node
  570.         //
  571.         // are people on our stack?
  572.         //
  573.         if (pHeader->pFrameCur)
  574.             return;
  575.         //
  576.         // if we are out of context then we should try again later
  577.         //
  578.         if (pHeader->dwThreadId != GetCurrentThreadId())
  579.         {
  580.             SendNotifyMessage(hWnd, WM_NULL, 0, 0L);
  581.             return;
  582.         }
  583.         //
  584.         // we keep the original window procedure as refdata for our
  585.         // CallOriginalWndProc subclass callback
  586.         //
  587.         pfnOldWndProc = (WNDPROC)pHeader->CallArray[0].dwRefData;
  588.         ASSERT(pfnOldWndProc);
  589.         //
  590.         // if somebody else is subclassed after us then we can't detach now
  591.         //
  592.         if (GetWindowProc(hWnd) != MasterSubclassProc)
  593.             return;
  594.         //
  595.         // go ahead and try to detach
  596.         //
  597.         if (!SubclassWindow(hWnd, pfnOldWndProc))
  598.         {
  599.             ASSERT(FALSE);      // just plain shouldn't happen
  600.             return;
  601.         }
  602.     }
  603.     //
  604.     // warn about anybody who hasn't unhooked yet
  605.     //
  606. #ifdef DEBUG
  607.     uCur = pHeader->uRefs;
  608.     pCall = pHeader->CallArray + uCur;
  609.     while (--uCur)          // don't complain about our 'call original' node
  610.     {
  611.         pCall--;
  612.         if (pCall->pfnSubclass)
  613.         {
  614.             //
  615.             // always warn about these they could be leaks
  616.             //
  617.             DebugMsg(TF_ALWAYS, TEXT("warning: orphan subclass: fn %08X, id %08X, dw %08X"),
  618.                 pCall->pfnSubclass, pCall->uIdSubclass, pCall->dwRefData);
  619.         }
  620.     }
  621. #endif
  622.     //
  623.     // free the header now
  624.     //
  625.     FreeSubclassHeader(hWnd, pHeader);
  626. }
  627. //-----------------------------------------------------------------------------
  628. // PurgeSingleCallNode
  629. //
  630. // this procedure purges a single dead node in the call array
  631. //
  632. //-----------------------------------------------------------------------------
  633. void PurgeSingleCallNode(HWND hWnd, SUBCLASS_HEADER *pHeader)
  634. {
  635.     UINT uRemain;
  636.     ASSERT(IS_VALID_STRUCT_PTR(pHeader, SUBCLASS_HEADER));
  637. #ifdef FREETHREADEDSUBCLASSGOOP
  638.     ASSERTCRITICAL;         // we will try to re-arrange the call array
  639. #else
  640.     ASSERT(IsWindowOnCurrentThread(hWnd));    
  641. #endif
  642.     
  643.     if (!pHeader->uCleanup) // a little sanity
  644.     {
  645.         ASSERT(FALSE);      // nothing to do!
  646.         return;
  647.     }
  648.     //
  649.     // and a little paranoia
  650.     //
  651.     ASSERT(!pHeader->pFrameCur ||
  652.         (pHeader->uCleanup < pHeader->pFrameCur->uDeepestCall));
  653.     //
  654.     // are there any call nodes above the one we're about to remove?
  655.     //
  656.     if ((uRemain = (pHeader->uRefs - pHeader->uCleanup)) > 0)
  657.     {
  658.         //
  659.         // yup, need to fix up the array the hard way
  660.         //
  661.         SUBCLASS_CALL *pCall;
  662.         SUBCLASS_FRAME *pFrame;
  663.         UINT uCur, uMax;
  664.         //
  665.         // move the remaining nodes down into the empty space
  666.         //
  667.         pCall = pHeader->CallArray + pHeader->uCleanup;
  668.         MoveMemory(pCall, pCall + 1, uRemain * sizeof(SUBCLASS_CALL));
  669.         ASSERT(IS_VALID_STRUCT_PTR(pCall, SUBCLASS_CALL));
  670.         //
  671.         // update the call indices of any active frames
  672.         //
  673.         uCur = pHeader->uCleanup;
  674.         pFrame = pHeader->pFrameCur;
  675.         while (pFrame)
  676.         {
  677.             if (pFrame->uCallIndex >= uCur)
  678.             {
  679.                 pFrame->uCallIndex--;
  680.                 if (pFrame->uDeepestCall >= uCur)
  681.                     pFrame->uDeepestCall--;
  682.             }
  683.             pFrame = pFrame->pFramePrev;
  684.         }
  685.         //
  686.         // now search for any other dead call nodes in the reamining area
  687.         //
  688.         uMax = pHeader->uRefs - 1;  // we haven't decremented uRefs yet
  689.         while (uCur < uMax)
  690.         {
  691.             if (!pCall->pfnSubclass)
  692.                 break;
  693.             pCall++;
  694.             uCur++;
  695.         }
  696.         pHeader->uCleanup = (uCur < uMax)? uCur : 0;
  697.     }
  698.     else
  699.     {
  700.         //
  701.         // nope, this case is easy
  702.         //
  703.         pHeader->uCleanup = 0;
  704.     }
  705.     //
  706.     // finally, decrement the client count
  707.     //
  708.     pHeader->uRefs--;
  709. }
  710. //-----------------------------------------------------------------------------
  711. // CompactSubclassHeader
  712. //
  713. // this procedure attempts to compact the subclass call array, freeing the
  714. // subclass header if the array is empty
  715. //
  716. //-----------------------------------------------------------------------------
  717. void CompactSubclassHeader(HWND hWnd, SUBCLASS_HEADER *pHeader)
  718. {
  719. #ifdef FREETHREADEDSUBCLASSGOOP
  720.     ASSERTCRITICAL;         // we will try to re-arrange the call array
  721. #else
  722.     ASSERT(IsWindowOnCurrentThread(hWnd));    
  723. #endif
  724.     ASSERT(IS_VALID_STRUCT_PTR(pHeader, SUBCLASS_HEADER));
  725.     //
  726.     // we must handle the "window destroyed unexpectedly during callback" case
  727.     //
  728.     if (hWnd)
  729.     {
  730.         //
  731.         // clean out as many dead callbacks as possible
  732.         //
  733.         while (pHeader->uCleanup && (!pHeader->pFrameCur ||
  734.             (pHeader->uCleanup < pHeader->pFrameCur->uDeepestCall)))
  735.         {
  736.             PurgeSingleCallNode(hWnd, pHeader);
  737.         }
  738.         //
  739.         // do we still have clients?
  740.         //
  741.         if (pHeader->uRefs > 1)
  742.         {
  743.             //
  744.             // yes, shrink our allocation, leaving room for at least one client
  745.             //
  746.             ReAllocSubclassHeader(hWnd, pHeader, pHeader->uRefs + 1);
  747.             return;
  748.         }
  749.     }
  750.     //
  751.     // try to detach and free
  752.     //
  753.     DetachSubclassHeader(hWnd, pHeader, FALSE);
  754. }
  755. //-----------------------------------------------------------------------------
  756. // FindCallRecord
  757. //
  758. // this procedure searches for a call record with the specified subclass proc
  759. // and id, and returns its address.  if no such call record is found then NULL
  760. // is returned.
  761. //
  762. //-----------------------------------------------------------------------------
  763. SUBCLASS_CALL *FindCallRecord(SUBCLASS_HEADER *pHeader,
  764.     SUBCLASSPROC pfnSubclass, WPARAM uIdSubclass)
  765. {
  766.     SUBCLASS_CALL *pCall;
  767.     UINT uCallIndex;
  768.     ASSERT(IS_VALID_STRUCT_PTR(pHeader, SUBCLASS_HEADER));
  769. #ifdef FREETHREADEDSUBCLASSGOOP
  770.     ASSERTCRITICAL;         // we'll be scanning the call array
  771. #endif
  772.     //
  773.     // scan the call array.  note that we assume there is always at least
  774.     // one member in the table (our CallOriginalWndProc record)
  775.     //
  776.     pCall = pHeader->CallArray + (uCallIndex = pHeader->uRefs);
  777.     do
  778.     {
  779.         uCallIndex--;
  780.         pCall--;
  781.         if ((pCall->pfnSubclass == pfnSubclass) &&
  782.             (pCall->uIdSubclass == uIdSubclass))
  783.         {
  784.             return pCall;
  785.         }
  786.     }
  787.     while (uCallIndex != (UINT)-1);
  788.     return NULL;
  789. }
  790. //-----------------------------------------------------------------------------
  791. // GetWindowSubclass
  792. //
  793. // this procedure retrieves the reference data for the specified window
  794. // subclass callback
  795. //
  796. //-----------------------------------------------------------------------------
  797. BOOL GetWindowSubclass(HWND hWnd, SUBCLASSPROC pfnSubclass, UINT_PTR uIdSubclass,
  798.     DWORD_PTR *pdwRefData)
  799. {
  800.     SUBCLASS_HEADER *pHeader;
  801.     SUBCLASS_CALL *pCall;
  802.     BOOL fResult = FALSE;
  803.     DWORD_PTR dwRefData = 0;
  804.     //
  805.     // sanity
  806.     //
  807.     if (!IsWindow(hWnd))
  808.     {
  809.         AssertMsg(FALSE, TEXT("error: GetWindowSubclass - %08X not a window"), hWnd);
  810.         goto ReturnResult;
  811.     }
  812.     //
  813.     // more sanity
  814.     //
  815.     if (!pfnSubclass
  816. #ifdef DEBUG
  817.         || IsBadCodePtr((PROC)pfnSubclass)
  818. #endif
  819.         )
  820.     {
  821.         AssertMsg(FALSE, TEXT("error: GetWindowSubclass - invalid callback %08X"), pfnSubclass);
  822.         goto ReturnResult;
  823.     }
  824. #ifdef FREETHREADEDSUBCLASSGOOP
  825.     ENTERCRITICAL;
  826. #else
  827.     ASSERT(IsWindowOnCurrentThread(hWnd));    
  828. #endif
  829.     
  830.     //
  831.     // if we've subclassed it and they are a client then get the refdata
  832.     //
  833.     if (((pHeader = GetSubclassHeader(hWnd)) != NULL) &&
  834.         ((pCall = FindCallRecord(pHeader, pfnSubclass, uIdSubclass)) != NULL))
  835.     {
  836.         //
  837.         // fetch the refdata and note success
  838.         //
  839.         dwRefData = pCall->dwRefData;
  840.         fResult = TRUE;
  841.     }
  842. #ifdef FREETHREADEDSUBCLASSGOOP
  843.     LEAVECRITICAL;
  844. #else
  845.     ASSERT(IsWindowOnCurrentThread(hWnd));    
  846. #endif
  847.     //
  848.     // we always fill in/zero pdwRefData regradless of result
  849.     //
  850. ReturnResult:
  851.     if (pdwRefData)
  852.         *pdwRefData = dwRefData;
  853.     return fResult;
  854. }
  855. //-----------------------------------------------------------------------------
  856. // SetWindowSubclass
  857. //
  858. // this procedure installs/updates a window subclass callback.  subclass
  859. // callbacks are identified by their callback address and id pair.  if the
  860. // specified callback/id pair is not yet installed then the procedure installs
  861. // the pair.  if the callback/id pair is already installed then this procedure
  862. // changes the refernce data for the pair.
  863. //
  864. //-----------------------------------------------------------------------------
  865. BOOL SetWindowSubclass(HWND hWnd, SUBCLASSPROC pfnSubclass, UINT_PTR uIdSubclass,
  866.     DWORD_PTR dwRefData)
  867. {
  868.     SUBCLASS_HEADER *pHeader;
  869.     SUBCLASS_CALL *pCall;
  870.     BOOL bResult;
  871.     //
  872.     // some sanity
  873.     //
  874.     if (!IsWindow(hWnd))
  875.     {
  876.         AssertMsg(FALSE, TEXT("error: SetWindowSubclass - %08X not a window"), hWnd);
  877.         return FALSE;
  878.     }
  879.     //
  880.     // more sanity
  881.     //
  882.     if (!pfnSubclass
  883. #ifdef DEBUG
  884.         || IsBadCodePtr((PROC)pfnSubclass)
  885. #endif
  886.         )
  887.     {
  888.         AssertMsg(FALSE, TEXT("error: SetWindowSubclass - invalid callback %08X"), pfnSubclass);
  889.         return FALSE;
  890.     }
  891.     bResult = FALSE;    // assume failure
  892.     //
  893.     // we party on the subclass call chain here
  894. #ifdef FREETHREADEDSUBCLASSGOOP
  895.     ENTERCRITICAL;
  896. #else
  897.     ASSERT(IsWindowOnCurrentThread(hWnd));    
  898. #endif
  899.     //
  900.     // actually subclass the window
  901.     //
  902.     if ((pHeader = AttachSubclassHeader(hWnd)) == NULL)
  903.         goto bail;
  904.     //
  905.     // find a call node for this caller
  906.     //
  907.     if ((pCall = FindCallRecord(pHeader, pfnSubclass, uIdSubclass)) == NULL)
  908.     {
  909.         //
  910.         // not found, alloc a new one
  911.         //
  912.         SUBCLASS_HEADER *pHeaderT =
  913.             ReAllocSubclassHeader(hWnd, pHeader, pHeader->uRefs + 1);
  914.         if (!pHeaderT)
  915.         {
  916.             //
  917.             // re-query in case it is already gone
  918.             //
  919.             if ((pHeader = FastGetSubclassHeader(hWnd)) != NULL)
  920.                 CompactSubclassHeader(hWnd, pHeader);
  921.             goto bail;
  922.         }
  923.         pHeader = pHeaderT;
  924.         pCall = pHeader->CallArray + pHeader->uRefs;
  925.         pHeader->uRefs++;
  926.     }
  927.     //
  928.     // fill in the subclass call data
  929.     //
  930.     pCall->pfnSubclass = pfnSubclass;
  931.     pCall->uIdSubclass = uIdSubclass;
  932.     pCall->dwRefData   = dwRefData;
  933.     bResult = TRUE;
  934. bail:
  935.     //
  936.     // release the critical section and return the result
  937.     //
  938. #ifdef FREETHREADEDSUBCLASSGOOP
  939.     LEAVECRITICAL;
  940. #else
  941.     ASSERT(IsWindowOnCurrentThread(hWnd));    
  942. #endif
  943.     return bResult;
  944. }
  945. //-----------------------------------------------------------------------------
  946. // RemoveWindowSubclass
  947. //
  948. // this procedure removes a subclass callback from a window.  subclass
  949. // callbacks are identified by their callback address and id pair.
  950. //
  951. //-----------------------------------------------------------------------------
  952. BOOL RemoveWindowSubclass(HWND hWnd, SUBCLASSPROC pfnSubclass,
  953.     UINT_PTR uIdSubclass)
  954. {
  955.     SUBCLASS_HEADER *pHeader;
  956.     SUBCLASS_CALL *pCall;
  957.     BOOL bResult;
  958.     UINT uCall;
  959.     //
  960.     // some sanity
  961.     //
  962.     if (!IsWindow(hWnd))
  963.     {
  964.         AssertMsg(FALSE, TEXT("error: RemoveWindowSubclass - %08X not a window"), hWnd);
  965.         return FALSE;
  966.     }
  967.     //
  968.     // more sanity
  969.     //
  970.     if (!pfnSubclass
  971. #ifdef DEBUG
  972.         || IsBadCodePtr((PROC)pfnSubclass)
  973. #endif
  974.         )
  975.     {
  976.         AssertMsg(FALSE, TEXT("error: RemoveWindowSubclass - invalid callback %08X"), pfnSubclass);
  977.         return FALSE;
  978.     }
  979.     bResult = FALSE;    // assume failure
  980.     //
  981.     // we party on the subclass call chain here
  982. #ifdef FREETHREADEDSUBCLASSGOOP
  983.     ENTERCRITICAL;
  984. #else
  985.     ASSERT(IsWindowOnCurrentThread(hWnd));    
  986. #endif
  987.     //
  988.     // obtain our subclass data
  989.     //
  990.     if ((pHeader = GetSubclassHeader(hWnd)) == NULL)
  991.         goto bail;
  992.     //
  993.     // find the callback to remove
  994.     //
  995.     if ((pCall = FindCallRecord(pHeader, pfnSubclass, uIdSubclass)) == NULL)
  996.         goto bail;
  997.     //
  998.     // disable this node and remember that we have something to clean up
  999.     //
  1000.     pCall->pfnSubclass = NULL;
  1001.     uCall = (UINT) (pCall - pHeader->CallArray);
  1002.     if (!pHeader->uCleanup || (uCall < pHeader->uCleanup))
  1003.         pHeader->uCleanup = uCall;
  1004.     //
  1005.     // now try to clean up any unused nodes
  1006.     //
  1007.     CompactSubclassHeader(hWnd, pHeader);
  1008. #ifdef DEBUG
  1009.     // the call above can realloc or free the subclass header for this window
  1010.     pHeader = NULL;
  1011. #endif
  1012.     bResult = TRUE;     // it worked
  1013. bail:
  1014.     //
  1015.     // release the critical section and return the result
  1016.     //
  1017. #ifdef FREETHREADEDSUBCLASSGOOP
  1018.     LEAVECRITICAL;
  1019. #else
  1020.     ASSERT(IsWindowOnCurrentThread(hWnd));    
  1021. #endif
  1022.     return bResult;
  1023. }
  1024. //-----------------------------------------------------------------------------
  1025. // DefSubclassProc
  1026. //
  1027. // this procedure calls the next handler in the window's subclass chain.  the
  1028. // last handler in the subclass chain is installed by us, and calls the
  1029. // original window procedure for the window.
  1030. //
  1031. //-----------------------------------------------------------------------------
  1032. LRESULT DefSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  1033. {
  1034.     SUBCLASS_HEADER *pHeader;
  1035.     LRESULT lResult = 0L;
  1036.     //
  1037.     // make sure the window is still valid
  1038.     //
  1039.     if (!IsWindow(hWnd))
  1040.     {
  1041.         AssertMsg(FALSE, TEXT("warning: DefSubclassProc - %08X not a window"), hWnd);
  1042.         goto BailNonCritical;
  1043.     }
  1044.     //
  1045.     // take the critical section while we figure out who to call next
  1046.     //
  1047. #ifdef FREETHREADEDSUBCLASSGOOP
  1048.     ENTERCRITICAL;
  1049. #else
  1050.     ASSERT(IsWindowOnCurrentThread(hWnd));    
  1051. #endif
  1052.         
  1053.     //
  1054.     // complain if we are being called improperly
  1055.     //
  1056.     if ((pHeader = FastGetSubclassHeader(hWnd)) == NULL)
  1057.     {
  1058.         AssertMsg(FALSE, TEXT("error: DefSubclassProc - window %08X not subclassed"), hWnd);
  1059.         goto BailCritical;
  1060.     }
  1061.     else if (GetCurrentThreadId() != pHeader->dwThreadId)
  1062.     {
  1063.         AssertMsg(FALSE, TEXT("error: DefSubclassProc - wrong thread for window %08X"), hWnd);
  1064.         goto BailCritical;
  1065.     }
  1066.     else if (!pHeader->pFrameCur)
  1067.     {
  1068.         AssertMsg(FALSE, TEXT("error: DefSubclassProc - window %08X not in callback"), hWnd);
  1069.         goto BailCritical;
  1070.     }
  1071.     //
  1072.     // call the next proc in the subclass chain
  1073.     //
  1074.     // WARNING: this call temporarily releases the critical section
  1075.     // WARNING: pHeader is invalid when this call returns
  1076.     //
  1077.     lResult = CallNextSubclassProc(pHeader, hWnd, uMsg, wParam, lParam);
  1078. #ifdef DEBUG
  1079.     pHeader = NULL;
  1080. #endif
  1081.     //
  1082.     // return the result
  1083.     //
  1084. BailCritical:
  1085. #ifdef FREETHREADEDSUBCLASSGOOP
  1086.     LEAVECRITICAL;
  1087. #else
  1088.     ASSERT(IsWindowOnCurrentThread(hWnd));    
  1089. #endif
  1090. BailNonCritical:
  1091.     return lResult;
  1092. }
  1093. //-----------------------------------------------------------------------------
  1094. // UpdateDeepestCall
  1095. //
  1096. // this procedure updates the deepest call index for the specified frame
  1097. //
  1098. //-----------------------------------------------------------------------------
  1099. void UpdateDeepestCall(SUBCLASS_FRAME *pFrame)
  1100. {
  1101. #ifdef FREETHREADEDSUBCLASSGOOP
  1102.     ASSERTCRITICAL;     // we are partying on the frame list
  1103. #endif
  1104.     if (pFrame->pFramePrev &&
  1105.         (pFrame->pFramePrev->uDeepestCall < pFrame->uCallIndex))
  1106.     {
  1107.         pFrame->uDeepestCall = pFrame->pFramePrev->uDeepestCall;
  1108.     }
  1109.     else
  1110.         pFrame->uDeepestCall = pFrame->uCallIndex;
  1111. }
  1112. //-----------------------------------------------------------------------------
  1113. // EnterSubclassFrame
  1114. //
  1115. // this procedure sets up a new subclass frame for the specified header, saving
  1116. // away the previous one
  1117. //
  1118. //-----------------------------------------------------------------------------
  1119. __inline void EnterSubclassFrame(SUBCLASS_HEADER *pHeader,
  1120.     SUBCLASS_FRAME *pFrame)
  1121. {
  1122. #ifdef FREETHREADEDSUBCLASSGOOP
  1123.     ASSERTCRITICAL;     // we are partying on the header and frame list
  1124. #endif
  1125.     //
  1126.     // fill in the frame and link it into the header
  1127.     //
  1128.     pFrame->uCallIndex   = pHeader->uRefs;
  1129.     pFrame->pFramePrev   = pHeader->pFrameCur;
  1130.     pFrame->pHeader      = pHeader;
  1131.     pHeader->pFrameCur   = pFrame;
  1132.     //
  1133.     // initialize the deepest call index for this frame
  1134.     //
  1135.     UpdateDeepestCall(pFrame);
  1136. }
  1137. //-----------------------------------------------------------------------------
  1138. // LeaveSubclassFrame
  1139. //
  1140. // this procedure cleans up the current subclass frame for the specified
  1141. // header, restoring the previous one
  1142. //
  1143. //-----------------------------------------------------------------------------
  1144. __inline SUBCLASS_HEADER *LeaveSubclassFrame(SUBCLASS_FRAME *pFrame)
  1145. {
  1146.     SUBCLASS_HEADER *pHeader;
  1147. #ifdef FREETHREADEDSUBCLASSGOOP
  1148.     ASSERTCRITICAL;     // we are partying on the header
  1149. #endif
  1150.     //
  1151.     // unlink the frame from its header (if it still exists)
  1152.     //
  1153.     if ((pHeader = pFrame->pHeader) != NULL)
  1154.         pHeader->pFrameCur = pFrame->pFramePrev;
  1155.     return pHeader;
  1156. }
  1157. //-----------------------------------------------------------------------------
  1158. // SubclassFrameException
  1159. //
  1160. // this procedure cleans up when an exception is thrown from a subclass frame
  1161. //
  1162. //-----------------------------------------------------------------------------
  1163. void SubclassFrameException(SUBCLASS_FRAME *pFrame)
  1164. {
  1165.     //
  1166.     // clean up the current subclass frame
  1167.     //
  1168. #ifdef FREETHREADEDSUBCLASSGOOP
  1169.     ENTERCRITICAL;
  1170. #endif
  1171.     DebugMsg(TF_ALWAYS, TEXT("warning: cleaning up subclass frame after exception"));
  1172.     LeaveSubclassFrame(pFrame);
  1173. #ifdef FREETHREADEDSUBCLASSGOOP
  1174.     LEAVECRITICAL;
  1175. #endif
  1176. }
  1177. //-----------------------------------------------------------------------------
  1178. // MasterSubclassProc
  1179. //
  1180. // this is the window procedure we install to dispatch subclass callbacks.
  1181. // it maintains a linked list of 'frames' through the stack which allow
  1182. // DefSubclassProc to call the right subclass procedure in multiple-message
  1183. // scenarios.
  1184. //
  1185. //-----------------------------------------------------------------------------
  1186. LRESULT CALLBACK MasterSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam,
  1187.     LPARAM lParam)
  1188. {
  1189.     SUBCLASS_FRAME Frame;
  1190.     SUBCLASS_HEADER *pHeader;
  1191.     LRESULT lResult = 0;
  1192.     //
  1193.     // prevent people from partying on the callback chain while we look at it
  1194.     //
  1195. #ifdef FREETHREADEDSUBCLASSGOOP
  1196.     ENTERCRITICAL;
  1197. #else
  1198.     ASSERT(IsWindowOnCurrentThread(hWnd));    
  1199. #endif
  1200.     //
  1201.     // freak out if we got here and we don't have our data
  1202.     //
  1203.     if ((pHeader = FastGetSubclassHeader(hWnd)) == NULL)
  1204.     {
  1205. #ifdef FREETHREADEDSUBCLASSGOOP
  1206.         LEAVECRITICAL;
  1207. #else
  1208.         ASSERT(IsWindowOnCurrentThread(hWnd));        
  1209. #endif
  1210.         return SubclassDeath(hWnd, uMsg, wParam, lParam);
  1211.     }
  1212.     //
  1213.     // set up a new subclass frame and save away the previous one
  1214.     //
  1215.     EnterSubclassFrame(pHeader, &Frame);
  1216.     __try   // protect our state information from exceptions
  1217.     {
  1218.         //
  1219.         // go ahead and call the subclass chain on this frame
  1220.         //
  1221.         // WARNING: this call temporarily releases the critical section
  1222.         // WARNING: pHeader is invalid when this call returns
  1223.         //
  1224.         lResult =
  1225.             CallNextSubclassProc(pHeader, hWnd, uMsg, wParam, lParam);
  1226. #ifdef DEBUG
  1227.         pHeader = NULL;
  1228. #endif
  1229.     }
  1230.     __except ((SubclassFrameException(&Frame), EXCEPTION_CONTINUE_SEARCH))
  1231.     {
  1232.         ASSERT(FALSE);
  1233.     }
  1234.     __endexcept
  1235. #ifdef FREETHREADEDSUBCLASSGOOP
  1236.     ASSERTCRITICAL;
  1237. #else
  1238.     ASSERT(IsWindowOnCurrentThread(hWnd));    
  1239. #endif
  1240.     //
  1241.     // restore the previous subclass frame
  1242.     //
  1243.     pHeader = LeaveSubclassFrame(&Frame);
  1244.     //
  1245.     // if the header is gone we have already cleaned up in a nested frame
  1246.     //
  1247.     if (!pHeader)
  1248.         goto BailOut;
  1249.     //
  1250.     // was the window nuked (somehow) without us seeing the WM_NCDESTROY?
  1251.     //
  1252.     if (!IsWindow(hWnd))
  1253.     {
  1254.         //
  1255.         // EVIL! somebody subclassed after us and didn't pass on WM_NCDESTROY
  1256.         //
  1257.         AssertMsg(FALSE, TEXT("unknown subclass proc swallowed a WM_NCDESTROY"));
  1258.         // go ahead and clean up now
  1259.         hWnd = NULL;
  1260.         uMsg = WM_NCDESTROY;
  1261.     }
  1262.     //
  1263.     // if we are returning from a WM_NCDESTROY then we need to clean up
  1264.     //
  1265.     if (uMsg == WM_NCDESTROY)
  1266.     {
  1267.         DetachSubclassHeader(hWnd, pHeader, TRUE);
  1268.         goto BailOut;
  1269.     }
  1270.     //
  1271.     // is there any pending cleanup, or are all our clients gone?
  1272.     //
  1273.     if (pHeader->uCleanup || (!pHeader->pFrameCur && (pHeader->uRefs <= 1)))
  1274.     {
  1275.         CompactSubclassHeader(hWnd, pHeader);
  1276. #ifdef DEBUG
  1277.         pHeader = NULL;
  1278. #endif
  1279.     }
  1280.     //
  1281.     // all done
  1282.     //
  1283. BailOut:
  1284. #ifdef FREETHREADEDSUBCLASSGOOP
  1285.     LEAVECRITICAL;
  1286. #endif
  1287. #ifdef FREETHREADEDSUBCLASSGOOP
  1288.     ASSERTNONCRITICAL;
  1289. #endif
  1290.     return lResult;
  1291. }
  1292. //-----------------------------------------------------------------------------
  1293. // EnterSubclassCallback
  1294. //
  1295. // this procedure finds the next callback in the subclass chain and updates
  1296. // pFrame to indicate that we are calling it
  1297. //
  1298. //-----------------------------------------------------------------------------
  1299. UINT EnterSubclassCallback(SUBCLASS_HEADER *pHeader, SUBCLASS_FRAME *pFrame,
  1300.     SUBCLASS_CALL *pCallChosen)
  1301. {
  1302.     SUBCLASS_CALL *pCall;
  1303.     UINT uDepth;
  1304.     //
  1305.     // we will be scanning the subclass chain and updating frame data
  1306.     //
  1307. #ifdef FREETHREADEDSUBCLASSGOOP
  1308.     ASSERTCRITICAL;
  1309. #endif
  1310.     //
  1311.     // scan the subclass chain for the next callable subclass callback
  1312.     //
  1313.     pCall = pHeader->CallArray + pFrame->uCallIndex;
  1314.     uDepth = 0;
  1315.     do
  1316.     {
  1317.         uDepth++;
  1318.         pCall--;
  1319.     } while (!pCall->pfnSubclass);
  1320.     //
  1321.     // copy the callback information for the caller
  1322.     //
  1323.     pCallChosen->pfnSubclass = pCall->pfnSubclass;
  1324.     pCallChosen->uIdSubclass = pCall->uIdSubclass;
  1325.     pCallChosen->dwRefData   = pCall->dwRefData;
  1326.     //
  1327.     // adjust the frame's call index by the depth we entered
  1328.     //
  1329.     pFrame->uCallIndex -= uDepth;
  1330.     //
  1331.     // keep the deepest call index up to date
  1332.     //
  1333.     UpdateDeepestCall(pFrame);
  1334.     return uDepth;
  1335. }
  1336. //-----------------------------------------------------------------------------
  1337. // LeaveSubclassCallback
  1338. //
  1339. // this procedure finds the next callback in the cal
  1340. //
  1341. //-----------------------------------------------------------------------------
  1342. __inline void LeaveSubclassCallback(SUBCLASS_FRAME *pFrame, UINT uDepth)
  1343. {
  1344.     //
  1345.     // we will be updating subclass frame data
  1346.     //
  1347. #ifdef FREETHREADEDSUBCLASSGOOP
  1348.     ASSERTCRITICAL;
  1349. #endif
  1350.     //
  1351.     // adjust the frame's call index by the depth we entered and return
  1352.     //
  1353.     pFrame->uCallIndex += uDepth;
  1354.     //
  1355.     // keep the deepest call index up to date
  1356.     //
  1357.     UpdateDeepestCall(pFrame);
  1358. }
  1359. //-----------------------------------------------------------------------------
  1360. // SubclassCallbackException
  1361. //
  1362. // this procedure cleans up when a subclass callback throws an exception
  1363. //
  1364. //-----------------------------------------------------------------------------
  1365. void SubclassCallbackException(SUBCLASS_FRAME *pFrame, UINT uDepth)
  1366. {
  1367.     //
  1368.     // clean up the current subclass callback
  1369.     //
  1370. #ifdef FREETHREADEDSUBCLASSGOOP
  1371.     ENTERCRITICAL;
  1372. #endif
  1373.     DebugMsg(TF_ALWAYS, TEXT("warning: cleaning up subclass callback after exception"));
  1374.     LeaveSubclassCallback(pFrame, uDepth);
  1375. #ifdef FREETHREADEDSUBCLASSGOOP
  1376.     LEAVECRITICAL;
  1377. #endif
  1378. }
  1379. //-----------------------------------------------------------------------------
  1380. // CallNextSubclassProc
  1381. //
  1382. // this procedure calls the next subclass callback in the subclass chain
  1383. //
  1384. // WARNING: this call temporarily releases the critical section
  1385. // WARNING: pHeader is invalid when this call returns
  1386. //
  1387. //-----------------------------------------------------------------------------
  1388. LRESULT CallNextSubclassProc(SUBCLASS_HEADER *pHeader, HWND hWnd, UINT uMsg,
  1389.     WPARAM wParam, LPARAM lParam)
  1390. {
  1391.     SUBCLASS_CALL Call;
  1392.     SUBCLASS_FRAME *pFrame;
  1393.     LRESULT lResult;
  1394.     UINT uDepth;
  1395. #ifdef FREETHREADEDSUBCLASSGOOP
  1396.     ASSERTCRITICAL;     // sanity
  1397. #endif
  1398.     ASSERT(pHeader);    // paranoia
  1399.     //
  1400.     // get the current subclass frame
  1401.     //
  1402.     pFrame = pHeader->pFrameCur;
  1403.     ASSERT(pFrame);
  1404.     //
  1405.     // get the next subclass call we need to make
  1406.     //
  1407.     uDepth = EnterSubclassCallback(pHeader, pFrame, &Call);
  1408.     //
  1409.     // leave the critical section so we don't deadlock in our callback
  1410.     //
  1411.     // WARNING: pHeader is invalid when this call returns
  1412.     //
  1413. #ifdef FREETHREADEDSUBCLASSGOOP
  1414.     LEAVECRITICAL;
  1415. #endif
  1416. #ifdef DEBUG
  1417.     pHeader = NULL;
  1418. #endif
  1419.     //
  1420.     // we call the outside world so prepare to deadlock if we have the critsec
  1421.     //
  1422. #ifdef FREETHREADEDSUBCLASSGOOP
  1423.     ASSERTNONCRITICAL;
  1424. #endif
  1425.     __try   // protect our state information from exceptions
  1426.     {
  1427.         //
  1428.         // call the chosen subclass proc
  1429.         //
  1430.         ASSERT(Call.pfnSubclass);
  1431.         lResult = Call.pfnSubclass(hWnd, uMsg, wParam, lParam,
  1432.             Call.uIdSubclass, Call.dwRefData);
  1433.     }
  1434.     __except ((SubclassCallbackException(pFrame, uDepth),
  1435.         EXCEPTION_CONTINUE_SEARCH))
  1436.     {
  1437.         ASSERT(FALSE);
  1438.     }
  1439.     __endexcept
  1440.     //
  1441.     // we left the critical section before calling out so re-enter it
  1442.     //
  1443. #ifdef FREETHREADEDSUBCLASSGOOP
  1444.     ENTERCRITICAL;
  1445. #endif
  1446.     
  1447.     //
  1448.     // finally, clean up and return
  1449.     //
  1450.     LeaveSubclassCallback(pFrame, uDepth);
  1451.     return lResult;
  1452. }
  1453. ///////////////////////////////////////////////////////////////////////////////
  1454. #if defined(RETAIL_ZOMBIE_MESSAGE_WNDPROC) || defined(DEBUG)
  1455. #ifdef DEBUG
  1456. static const TCHAR c_szZombieMessage[] =                                     
  1457.     TEXT("This window has encountered an internal error which is preventing ")    
  1458.     TEXT("it from operating normally.rnnPlease report this problem to ")       
  1459.     TEXT("FrancisH immediately.");
  1460. #else
  1461. static const TCHAR c_szZombieMessage[] =                                     
  1462.     TEXT("This window has encountered an internal error which is preventing ")    
  1463.     TEXT("it from operating normally.rnnPlease report this as a bug in the ")  
  1464.     TEXT("COMCTL32 library.");
  1465. #endif
  1466. LRESULT ZombieWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  1467. {
  1468.     switch (uMsg)
  1469.     {
  1470.     case WM_ERASEBKGND:
  1471.         {
  1472.             HDC hDC = (HDC)wParam;
  1473.             HBRUSH hBrush = CreateSolidBrush(RGB(255,255,0));
  1474.             if (hBrush)
  1475.             {
  1476.                 RECT rcErase;
  1477.                 switch (GetClipBox(hDC, &rcErase))
  1478.                 {
  1479.                 default:
  1480.                     FillRect(hDC, &rcErase, hBrush);
  1481.                     break;
  1482.                 case NULLREGION:
  1483.                 case ERROR:
  1484.                     break;
  1485.                 }
  1486.                 DeleteBrush(hBrush);
  1487.             }
  1488.         }
  1489.         return 1;
  1490.     case WM_PAINT:
  1491.         {
  1492.             RECT rcClient;
  1493.             PAINTSTRUCT ps;
  1494.             HDC hDC = BeginPaint(hWnd, &ps);
  1495.             if (hDC && GetClientRect(hWnd, &rcClient))
  1496.             {
  1497.                 COLORREF clrBkSave = SetBkColor(hDC, RGB(255,255,0));
  1498.                 COLORREF clrFgSave = SetTextColor(hDC, RGB(255,0,0));
  1499.                 DrawText(hDC, c_szZombieMessage, -1, &rcClient,
  1500.                     DT_LEFT | DT_TOP | DT_NOPREFIX | DT_WORDBREAK |
  1501.                     DT_WORD_ELLIPSIS);
  1502.                 SetTextColor(hDC, clrFgSave);
  1503.                 SetBkColor(hDC, clrBkSave);
  1504.             }
  1505.             EndPaint(hWnd, &ps);
  1506.         }
  1507.         return 0;
  1508.     }
  1509.     return DefWindowProc(hWnd, uMsg, wParam, lParam);
  1510. }
  1511. #endif
  1512. ///////////////////////////////////////////////////////////////////////////////