Leaked source code of windows server 2003
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.

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