Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1716 lines
48 KiB

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