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.

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