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.

2038 lines
58 KiB

  1. /*****************************************************************************
  2. *
  3. * DISubCls.c
  4. *
  5. * Copyright (c) 1996 Microsoft Corporation. All Rights Reserved.
  6. *
  7. * Abstract:
  8. *
  9. * "Safe subclassing" code, stolen from comctl32.
  10. *
  11. * Originally written by francish. Stolen by raymondc.
  12. *
  13. * Contents:
  14. *
  15. * SetWindowSubclass
  16. * GetWindowSubclass
  17. * RemoveWindowSubclass
  18. * DefSubclassProc
  19. *
  20. *****************************************************************************/
  21. #include "dinputpr.h"
  22. /*****************************************************************************
  23. *
  24. * The sqiffle for this file.
  25. *
  26. *****************************************************************************/
  27. #define sqfl sqflSubclass
  28. /*****************************************************************************
  29. *
  30. * @doc INTERNAL
  31. *
  32. * @topic DirectInput Subclassing |
  33. *
  34. *
  35. * This module defines helper functions that make subclassing windows safe(er)
  36. * and easy(er). The code maintains a single property on the subclassed window
  37. * and dispatches various "subclass callbacks" to its clients a required. The
  38. * client is provided reference data and a simple "default processing" API.
  39. *
  40. * Semantics:
  41. * A "subclass callback" is identified by a unique pairing of a callback
  42. * function pointer and an unsigned ID value. Each callback can also store a
  43. * single DWORD of reference data, which is passed to the callback function
  44. * when it is called to filter messages. No reference counting is performed
  45. * for the callback, it may repeatedly call the SetWindowSubclass API to alter
  46. * the value of its reference data element as desired.
  47. *
  48. *****************************************************************************/
  49. /*****************************************************************************
  50. *
  51. * @doc INTERNAL
  52. *
  53. * @struct SUBCLASS_CALL |
  54. *
  55. * Structure which tracks a single subclassing client.
  56. *
  57. * Although a linked list would have made the code slightly
  58. * simpler, this module uses a packed callback array to avoid
  59. * unneccessary fragmentation.
  60. *
  61. * @field SUBCLASSPROC | pfnSubclass |
  62. *
  63. * The subclass procedure. If this is zero, it means that
  64. * the node is dying and should be ignored.
  65. *
  66. * @field UINT | uIdSubclass |
  67. *
  68. * Unique subclass identifier.
  69. *
  70. * @field DWORD | dwRefData |
  71. *
  72. * Optional reference data for subclass procedure.
  73. *
  74. *****************************************************************************/
  75. typedef struct SUBCLASS_CALL {
  76. SUBCLASSPROC pfnSubclass;
  77. UINT_PTR uIdSubclass;
  78. ULONG_PTR dwRefData;
  79. } SUBCLASS_CALL, *PSUBCLASS_CALL;
  80. /*****************************************************************************
  81. *
  82. * @doc INTERNAL
  83. *
  84. * @struct SUBCLASS_FRAME |
  85. *
  86. * Structure which tracks the state of an active call to the
  87. * window's window procedure.
  88. *
  89. * Each time the window procedure is entered, we create a new
  90. * <t SUBCLASS_FRAME>, which remains active until the last
  91. * subclass procedure returns, at which point the frame is
  92. * torn down.
  93. *
  94. * The subclass frames are stored on the stack. So walking
  95. * the frame chain causes you to wander through the stack.
  96. *
  97. * @field UINT | uCallIndex |
  98. *
  99. * Index of next callback to call.
  100. *
  101. * @field UINT | uDeepestCall |
  102. *
  103. * Deepest <e SUBCLASS_FRAME.uCallIndex> on the stack.
  104. *
  105. * @field SUBCLASS_FRAME * | pFramePrev |
  106. *
  107. * The previous subclass frame.
  108. *
  109. * @field PSUBCLASS_HEADER | pHeader |
  110. *
  111. * The header associated with this frame.
  112. *
  113. *****************************************************************************/
  114. typedef struct SUBCLASS_FRAME {
  115. UINT uCallIndex;
  116. UINT uDeepestCall;
  117. struct SUBCLASS_FRAME *pFramePrev;
  118. struct SUBCLASS_HEADER *pHeader;
  119. } SUBCLASS_FRAME, *PSUBCLASS_FRAME;
  120. /*****************************************************************************
  121. *
  122. * @doc INTERNAL
  123. *
  124. * @struct SUBCLASS_HEADER |
  125. *
  126. * Structure which tracks the subclass goo associated with
  127. * a window. A pointer to this structure is kept in a private
  128. * window property.
  129. *
  130. * @field UINT | uRefs |
  131. *
  132. * Subclass count. This is the number of valid entries
  133. * in the <p CallArray>.
  134. *
  135. * @field UINT | uAlloc |
  136. *
  137. * Number of allocated <t SUBCLASS_CALL> nodes in the array.
  138. *
  139. * @field UINT | uCleanup |
  140. *
  141. * Index of the call node to clean up.
  142. *
  143. * @field WORD | dwThreadId |
  144. *
  145. * Thread id of the window with which the structure is associated.
  146. *
  147. * @field PSUBCLASS_FRAME | pFrameCur |
  148. *
  149. * Pointer to the current subclass frame.
  150. *
  151. * @field SUBCLASS_CALL | CallArray[1] |
  152. *
  153. * Base of the packed call node array.
  154. *
  155. *****************************************************************************/
  156. typedef struct SUBCLASS_HEADER {
  157. UINT uRefs;
  158. UINT uAlloc;
  159. UINT uCleanup;
  160. DWORD dwThreadId;
  161. PSUBCLASS_FRAME pFrameCur;
  162. SUBCLASS_CALL CallArray[1];
  163. } SUBCLASS_HEADER, *PSUBCLASS_HEADER;
  164. #define CALLBACK_ALLOC_GRAIN (3) /* 1 defproc, 1 subclass, 1 spare */
  165. LRESULT CALLBACK
  166. MasterSubclassProc(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp);
  167. LRESULT INTERNAL
  168. CallNextSubclassProc(PSUBCLASS_HEADER pHeader, HWND hwnd, UINT wm,
  169. WPARAM wp, LPARAM lp);
  170. /*****************************************************************************
  171. *
  172. * @doc INTERNAL
  173. *
  174. * @func LRESULT | SubclassDeath |
  175. *
  176. * This function is called if we ever enter one of our subclassing
  177. * procedures without our reference data (and hence without the
  178. * previous <t WNDPROC>).
  179. *
  180. * Hitting this represents a catastrophic failure in the
  181. * subclass code.
  182. *
  183. * The function resets the <t WNDPROC> of the window to
  184. * <f DefWindowProc> to avoid faulting.
  185. *
  186. * @parm HWND | hwnd |
  187. *
  188. * Window that just got hosed.
  189. *
  190. * @parm UINT | wm |
  191. *
  192. * Window message that caused us to realize that we are hosed.
  193. *
  194. * @parm WPARAM | wp |
  195. *
  196. * Meaning depends on window message.
  197. *
  198. * @parm LPARAM | lp |
  199. *
  200. * Meaning depends on window message.
  201. *
  202. *****************************************************************************/
  203. LRESULT INTERNAL
  204. SubclassDeath(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp)
  205. {
  206. /*
  207. * WE SHOULD NEVER EVER GET HERE
  208. */
  209. // 7/19/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
  210. SquirtSqflPtszV(sqfl | sqflError,
  211. TEXT("Fatal! SubclassDeath in window %p"),
  212. hwnd);
  213. AssertF(0);
  214. /*
  215. * We call the outside world, so we'd better not have the critsec.
  216. */
  217. AssertF(!InCrit());
  218. /*
  219. * In theory, we could save the original WNDPROC in a separate property,
  220. * but that just wastes memory for something that should never happen.
  221. */
  222. #ifdef WINNT
  223. SetWindowLongPtr( hwnd, GWLP_WNDPROC, (LONG_PTR)(DefWindowProc));
  224. #else
  225. SubclassWindow(hwnd, DefWindowProc);
  226. #endif
  227. return DefWindowProc(hwnd, wm, wp, lp);
  228. }
  229. /*****************************************************************************
  230. *
  231. * @doc INTERNAL
  232. *
  233. * @func WNDPROC | GetWindowProc |
  234. *
  235. * Returns the <t WNDPROC> of the specified window.
  236. *
  237. * @parm HWND | hwnd |
  238. *
  239. * Window to be inspected.
  240. *
  241. * @returns
  242. *
  243. * The <t WNDPROC> of the specified window.
  244. *
  245. *****************************************************************************/
  246. WNDPROC INLINE
  247. GetWindowProc(HWND hwnd)
  248. {
  249. #ifdef WINNT
  250. return (WNDPROC)GetWindowLongPtr(hwnd, GWLP_WNDPROC);
  251. #else
  252. return (WNDPROC)GetWindowLong(hwnd, GWL_WNDPROC);
  253. #endif
  254. }
  255. /*****************************************************************************
  256. *
  257. * @doc INTERNAL
  258. *
  259. * @global ATOM | g_atmDISubclass |
  260. *
  261. * This is the global <t ATOM> we use to store our
  262. * <t SUBCLASS_HEADER> property on whatever windows come our way.
  263. *
  264. * If the <p WIN95_HACK> symbol is defined, then we will work
  265. * around a bug in Windows 95 where Windows "helpfully"
  266. * <f GlobalDeleteAtom>'s all properties that are on a window
  267. * when the window dies. See Francis's original explanation
  268. * in subclass.c.
  269. *
  270. *****************************************************************************/
  271. #pragma BEGIN_CONST_DATA
  272. TCHAR c_tszDISubclass[] = TEXT("DirectInputSubclassInfo");
  273. #pragma END_CONST_DATA
  274. #ifdef WIN95_HACK
  275. ATOM g_atmDISubclass;
  276. #endif
  277. /*****************************************************************************
  278. *
  279. * @doc INTERNAL
  280. *
  281. * @func PSUBCLASS_HEADER | FastGetSubclassHeader |
  282. *
  283. * Obtains the <t SUBCLASS_HEADER> for the specified window.
  284. *
  285. * This function succeeds on any thread, although the value
  286. * is meaningless from the wrong process.
  287. *
  288. * @parm HWND | hwnd |
  289. *
  290. * Window in question.
  291. *
  292. * @returns
  293. *
  294. * Pointer to the <t SUBCLASS_HEADER> associated with the window,
  295. * or <c NULL> if the window is not subclassed by us.
  296. *
  297. *****************************************************************************/
  298. PSUBCLASS_HEADER INLINE
  299. FastGetSubclassHeader(HWND hwnd)
  300. {
  301. #ifdef WIN95_HACK
  302. /*
  303. * The right thing happens if g_atmDISubclass is 0, namely,
  304. * the property is not found. Unfortunately, NT RIPs when
  305. * you do this, so we'll be polite and not RIP.
  306. */
  307. if (g_atmDISubclass) {
  308. return (PSUBCLASS_HEADER)GetProp(hWnd, (PV)g_atmDISubclass);
  309. } else {
  310. return 0;
  311. }
  312. #else
  313. return (PSUBCLASS_HEADER)GetProp(hwnd, c_tszDISubclass);
  314. #endif
  315. }
  316. /*****************************************************************************
  317. *
  318. * @doc INTERNAL
  319. *
  320. * @func PSUBCLASS_HEADER | GetSubclassHeader |
  321. *
  322. * Obtains the <t SUBCLASS_HEADER> for the specified window.
  323. * It fails if the caller is in the wrong process, but will
  324. * allow the caller to get the header from a different thread.
  325. *
  326. * @parm HWND | hwnd |
  327. *
  328. * Window in question.
  329. *
  330. * @returns
  331. *
  332. * Pointer to the <t SUBCLASS_HEADER> associated with the window,
  333. * or <c NULL> if the window is not subclass by us yet, or 1
  334. * if it belongs to another process.
  335. *
  336. *****************************************************************************/
  337. PSUBCLASS_HEADER INTERNAL
  338. GetSubclassHeader(HWND hwnd)
  339. {
  340. DWORD idProcess;
  341. /*
  342. * Make sure we're in the right process.
  343. *
  344. * Must use our helper function to catch bad scenarios like
  345. * the goofy Windows 95 console window which lies about its
  346. * owner.
  347. */
  348. idProcess = GetWindowPid(hwnd);
  349. if (idProcess == GetCurrentProcessId()) { /* In the right process */
  350. return FastGetSubclassHeader(hwnd);
  351. } else {
  352. if (idProcess) {
  353. // 7/19/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
  354. SquirtSqflPtszV(sqfl | sqflError,
  355. TEXT("XxxWindowSubclass: ")
  356. TEXT("wrong process for window %p"), hwnd);
  357. }
  358. return (PSUBCLASS_HEADER)1;
  359. }
  360. }
  361. /*****************************************************************************
  362. *
  363. * @doc INTERNAL
  364. *
  365. * @func BOOL | SetSubclassHeader |
  366. *
  367. * Sets the <t SUBCLASS_HEADER> for the specified window.
  368. *
  369. * @parm HWND | hwnd |
  370. *
  371. * Window in question.
  372. *
  373. * @parm PSUBCLASS_HEADER | pHeader |
  374. *
  375. * The value to set.
  376. *
  377. * @parm PSUBCLASS_FRAME | pFrameFixup |
  378. *
  379. * The active frames, which need to be walked and fixed up
  380. * to refer to the new <t SUBCLASS_HEADER>.
  381. *
  382. *****************************************************************************/
  383. BOOL INTERNAL
  384. SetSubclassHeader(HWND hwnd, PSUBCLASS_HEADER pHeader,
  385. PSUBCLASS_FRAME pFrameFixup)
  386. {
  387. BOOL fRc;
  388. AssertF(InCrit()); /* We are partying on the header and frame list */
  389. #ifdef WIN95_HACK
  390. if (g_atmDISubclass == 0) {
  391. ATOM atm;
  392. /*
  393. * HACK: we are intentionally incrementing the refcount on this atom
  394. * WE DO NOT WANT IT TO GO BACK DOWN so we will not delete it in
  395. * process detach (see comments for g_atmDISubclass in subclass.c
  396. * for more info).
  397. */
  398. atm = GlobalAddAtom(c_tszDISubclass);
  399. if (atm) {
  400. g_atmDISubclass = atm; /* In case the old atom got nuked */
  401. }
  402. }
  403. #endif
  404. /*
  405. * Update the frame list if required.
  406. */
  407. while (pFrameFixup) {
  408. pFrameFixup->pHeader = pHeader;
  409. pFrameFixup = pFrameFixup->pFramePrev;
  410. }
  411. /*
  412. * If we have a window to update, then update/remove the property
  413. * as required.
  414. */
  415. if (hwnd) {
  416. if (!pHeader) {
  417. #ifdef WIN95_HACK
  418. /*
  419. * HACK: we remove with an ATOM so the refcount won't drop
  420. * (see comments for g_atmDISubclass above)
  421. */
  422. RemoveProp(hwnd, (PV)g_atmDISubclass);
  423. #else
  424. // 7/19/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
  425. SquirtSqflPtszV(sqfl, TEXT("SetSubclassHeader: Removing %p"),
  426. pHeader);
  427. RemoveProp(hwnd, c_tszDISubclass);
  428. #endif
  429. fRc = 1;
  430. } else {
  431. #ifdef WIN95_HACK
  432. /*
  433. * HACK: we add using a STRING so the refcount will go up
  434. * (see comments for g_atmDISubclass above)
  435. */
  436. #endif
  437. // 7/19/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
  438. SquirtSqflPtszV(sqfl, TEXT("SetSubclassHeader: Adding %p"),
  439. pHeader);
  440. fRc = SetProp(hwnd, c_tszDISubclass, pHeader);
  441. if (!fRc) {
  442. // 7/19/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
  443. SquirtSqflPtszV(sqfl | sqflError, TEXT("SetWindowSubclass: ")
  444. TEXT("couldn't subclass window %p"), hwnd);
  445. }
  446. }
  447. } else {
  448. fRc = 1; /* Weird vacuous success */
  449. }
  450. return fRc;
  451. }
  452. /*****************************************************************************
  453. *
  454. * @doc INTERNAL
  455. *
  456. * @func void | FreeSubclassHeader |
  457. *
  458. * Toss the subclass header for the specified window.
  459. *
  460. * @parm HWND | hwnd |
  461. *
  462. * Window in question.
  463. *
  464. * @parm PSUBCLASS_HEADER | pHeader |
  465. *
  466. * The value being tossed.
  467. *
  468. *****************************************************************************/
  469. void INTERNAL
  470. FreeSubclassHeader(HWND hwnd, PSUBCLASS_HEADER pHeader)
  471. {
  472. AssertF(InCrit()); /* we will be removing the subclass header */
  473. /*
  474. * Sanity checking...
  475. */
  476. if (pHeader) {
  477. // 7/19/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
  478. SquirtSqflPtszV(sqfl, TEXT("FreeSubclassHeader: Freeing %p"),
  479. pHeader);
  480. SetSubclassHeader(hwnd, 0, pHeader->pFrameCur); /* Clean up the header */
  481. LocalFree(pHeader);
  482. } else {
  483. AssertF(0);
  484. }
  485. }
  486. /*****************************************************************************
  487. *
  488. * @doc INTERNAL
  489. *
  490. * @func void | ReallocSubclassHeader |
  491. *
  492. * Change the size of the subclass header as indicated.
  493. *
  494. * @parm HWND | hwnd |
  495. *
  496. * Window in question.
  497. *
  498. * @parm PSUBCLASS_HEADER | pHeader |
  499. *
  500. * The current header.
  501. *
  502. * @parm UINT | uCallbacks |
  503. *
  504. * Desired size.
  505. *
  506. *****************************************************************************/
  507. PSUBCLASS_HEADER INTERNAL
  508. ReAllocSubclassHeader(HWND hwnd, PSUBCLASS_HEADER pHeader, UINT uCallbacks)
  509. {
  510. UINT uAlloc;
  511. AssertF(InCrit()); /* we will be replacing the subclass header */
  512. /*
  513. * Granularize the allocation.
  514. */
  515. uAlloc = CALLBACK_ALLOC_GRAIN *
  516. ((uCallbacks + CALLBACK_ALLOC_GRAIN - 1) / CALLBACK_ALLOC_GRAIN);
  517. /*
  518. * Do we need to change the allocation?
  519. */
  520. if (!pHeader || (uAlloc != pHeader->uAlloc)) {
  521. /*
  522. * compute bytes required
  523. */
  524. uCallbacks = uAlloc * sizeof(SUBCLASS_CALL) + sizeof(SUBCLASS_HEADER);
  525. /*
  526. * And try to alloc / realloc.
  527. */
  528. if (SUCCEEDED(ReallocCbPpv(uCallbacks, &pHeader))) {
  529. /*
  530. * Update info.
  531. */
  532. pHeader->uAlloc = uAlloc;
  533. if (SetSubclassHeader(hwnd, pHeader, pHeader->pFrameCur)) {
  534. } else {
  535. FreeSubclassHeader(hwnd, pHeader);
  536. pHeader = 0;
  537. }
  538. } else {
  539. pHeader = 0;
  540. }
  541. }
  542. AssertF(pHeader);
  543. return pHeader;
  544. }
  545. /*****************************************************************************
  546. *
  547. * @doc INTERNAL
  548. *
  549. * @func LRESULT | CallOriginalWndProc |
  550. *
  551. * This procedure is the default <t SUBCLASSPROC> which is always
  552. * installed when we subclass a window. The original window
  553. * procedure is installed as the reference data for this
  554. * callback. It simply calls the original <t WNDPROC> and
  555. * returns its result.
  556. *
  557. * @parm HWND | hwnd |
  558. *
  559. * Window in question.
  560. *
  561. * @parm UINT | wm |
  562. *
  563. * Window message that needs to go to the original <t WNDPROC>.
  564. *
  565. * @parm WPARAM | wp |
  566. *
  567. * Meaning depends on window message.
  568. *
  569. * @parm LPARAM | lp |
  570. *
  571. * Meaning depends on window message.
  572. *
  573. * @parm UINT | uIdSubclass |
  574. *
  575. * ID number (not used).
  576. *
  577. * @parm DWORD | dwRefData |
  578. *
  579. * Reference data for subclass procedure (original <t WNDPROC>).
  580. *
  581. *****************************************************************************/
  582. LRESULT CALLBACK
  583. CallOriginalWndProc(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp,
  584. UINT_PTR uIdSubclass, ULONG_PTR dwRefData)
  585. {
  586. /*
  587. * dwRefData should be the original window procedure
  588. */
  589. AssertF(dwRefData);
  590. /*
  591. * and call it.
  592. */
  593. return CallWindowProc((WNDPROC)dwRefData, hwnd, wm, wp, lp);
  594. }
  595. /*****************************************************************************
  596. *
  597. * @doc INTERNAL
  598. *
  599. * @func PSUBCLASS_HEADER | AttachSubclassHeader |
  600. *
  601. * This procedure makes sure that a given window is subclassed by us.
  602. * It maintains a reference count on the data structures associated
  603. * with our subclass. if the window is not yet subclassed by us
  604. * then this procedure installs our subclass procedure and
  605. * associated data structures.
  606. *
  607. * @parm HWND | hwnd |
  608. *
  609. * Window in question.
  610. *
  611. *****************************************************************************/
  612. PSUBCLASS_HEADER INTERNAL
  613. AttachSubclassHeader(HWND hwnd)
  614. {
  615. PSUBCLASS_HEADER pHeader;
  616. /*
  617. * We party on the subclass call chain here
  618. */
  619. AssertF(InCrit());
  620. /*
  621. * Yes, we subclass the window out of context, but we are careful
  622. * to avoid race conditions. There is still a problem if some
  623. * other DLL tries to un-subclass a window just as we are subclassing
  624. * it. But there's nothing you can do about it, and besides,
  625. * what are the odds...?
  626. */
  627. /*
  628. * If haven't already subclassed the window then do it now
  629. */
  630. pHeader = GetSubclassHeader(hwnd);
  631. if( pHeader == (PSUBCLASS_HEADER)1 )
  632. {
  633. /*
  634. * It's all gone horribly wrong
  635. * This can happen when the application uses joyXXX functions in Winmm.dll.
  636. */
  637. pHeader = 0;
  638. }
  639. else if (pHeader == 0) {
  640. /*
  641. * attach our header data to the window
  642. * we need space for two callbacks:
  643. * the subclass and the original proc
  644. */
  645. pHeader = ReAllocSubclassHeader(hwnd, 0, 2);
  646. if (pHeader) {
  647. SUBCLASS_CALL *pCall;
  648. /*
  649. * Set up the first node in the array to call
  650. * the original WNDPROC. Do this before subclassing
  651. * to avoid a race if the window receives a message
  652. * after we have installed our subclass but before
  653. * we can save the original WNDPROC.
  654. */
  655. AssertF(pHeader->uAlloc);
  656. pCall = pHeader->CallArray;
  657. pCall->pfnSubclass = CallOriginalWndProc;
  658. pCall->uIdSubclass = 0;
  659. pCall->dwRefData = (ULONG_PTR)GetWindowProc(hwnd);
  660. /*
  661. * init our subclass refcount...
  662. */
  663. pHeader->uRefs = 1;
  664. pHeader->dwThreadId = GetWindowThreadProcessId(hwnd, NULL);
  665. /*
  666. * Super-paranoid. We must must not race with another
  667. * instance of ourselves trying to un-subclass.
  668. */
  669. AssertF(InCrit());
  670. /*
  671. * Save the new "old" wndproc in case we raced with
  672. * somebody else trying to subclass.
  673. */
  674. #ifdef WINNT
  675. pCall->dwRefData = (ULONG_PTR)SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)MasterSubclassProc);
  676. #else
  677. pCall->dwRefData = (DWORD)SubclassWindow(hwnd, MasterSubclassProc);
  678. #endif
  679. if (pCall->dwRefData) {
  680. DllLoadLibrary(); /* Make sure we don't get unloaded */
  681. } else { /* clean up and get out */
  682. FreeSubclassHeader(hwnd, pHeader);
  683. pHeader = 0;
  684. }
  685. }
  686. }
  687. return pHeader;
  688. }
  689. /*****************************************************************************
  690. *
  691. * @doc INTERNAL
  692. *
  693. * @func void | DetachSubclassHeader |
  694. *
  695. * This procedure attempts to detach the subclass header from
  696. * the specified window.
  697. *
  698. * @parm HWND | hwnd |
  699. *
  700. * Window in question.
  701. *
  702. * @parm PSUBCLASS_HEADER | pHeader |
  703. *
  704. * Header to detach.
  705. *
  706. * @parm BOOL | fForce |
  707. *
  708. * Nonzero if we should detach even if we are not the top-level
  709. * subclass.
  710. *
  711. *****************************************************************************/
  712. void INTERNAL
  713. DetachSubclassHeader(HWND hwnd, PSUBCLASS_HEADER pHeader, BOOL fForce)
  714. {
  715. WNDPROC wndprocOld;
  716. AssertF(InCrit()); /* we party on the subclass call chain here */
  717. AssertF(pHeader); /* fear */
  718. /*
  719. * If we are not being forced to remove and the window is still
  720. * valid then sniff around a little and decide if it's a good
  721. * idea to detach now.
  722. */
  723. if (!fForce && hwnd) {
  724. AssertF(pHeader == FastGetSubclassHeader(hwnd)); /* paranoia */
  725. /* should always have the "call original" node */
  726. AssertF(pHeader->uRefs);
  727. /*
  728. * We can't have active clients.
  729. * We can't have people still on our stack.
  730. */
  731. if (pHeader->uRefs <= 1 && !pHeader->pFrameCur) {
  732. /*
  733. * We must be in the correct context.
  734. */
  735. if (pHeader->dwThreadId == GetCurrentThreadId()) {
  736. /*
  737. * We kept the original window procedure as refdata for our
  738. * CallOriginalWndProc subclass callback.
  739. */
  740. wndprocOld = (WNDPROC)pHeader->CallArray[0].dwRefData;
  741. AssertF(wndprocOld);
  742. /*
  743. * Make sure we are the top of the subclass chain.
  744. */
  745. if (GetWindowProc(hwnd) == MasterSubclassProc) {
  746. /*
  747. * go ahead and try to detach
  748. */
  749. #ifdef WINNT
  750. if (SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)wndprocOld)) {
  751. #else
  752. if (SubclassWindow(hwnd, wndprocOld)) {
  753. #endif
  754. SquirtSqflPtszV(sqfl, TEXT("DetachSubclassHeader: ")
  755. TEXT("Unhooked"));
  756. } else {
  757. AssertF(0); /* just plain shouldn't happen */
  758. goto failed;
  759. }
  760. } else { /* Not at top of chain; can't do it */
  761. SquirtSqflPtszV(sqfl, TEXT("DetachSubclassHeader: ")
  762. TEXT("Somebody else subclassed"));
  763. goto failed;
  764. }
  765. } else { /* Out of context. Try again later. */
  766. SendNotifyMessage(hwnd, WM_NULL, 0, 0L);
  767. goto failed;
  768. }
  769. } else {
  770. // 7/18/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
  771. SquirtSqflPtszV(sqfl, TEXT("DetachSubclassHeader: ")
  772. TEXT("Still %d users, %p frame"),
  773. pHeader->uRefs, pHeader->pFrameCur);
  774. goto failed;
  775. }
  776. }
  777. #if 0
  778. #ifdef DEBUG
  779. {
  780. /*
  781. * warn about anybody who hasn't unhooked yet
  782. */
  783. UINT uCur;
  784. SUBCLASS_CALL *pCall;
  785. uCur = pHeader->uRefs;
  786. pCall = pHeader->CallArray + uCur;
  787. /* don't complain about our 'call original' node */
  788. while (--uCur) {
  789. pCall--;
  790. if (pCall->pfnSubclass) {
  791. /*
  792. * always warn about these they could be leaks
  793. */
  794. // 7/19/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
  795. SquirtSqflPtszV(sqfl | sqflError, TEXT("warning: orphan subclass: ")
  796. TEXT("fn %p, id %08x, dw %08x"),
  797. pCall->pfnSubclass, pCall->uIdSubclass,
  798. pCall->dwRefData);
  799. }
  800. }
  801. }
  802. #endif
  803. #endif
  804. /*
  805. * free the header now
  806. */
  807. FreeSubclassHeader(hwnd, pHeader);
  808. DllFreeLibrary(); /* Undo LoadLibrary when we hooked */
  809. failed:;
  810. }
  811. /*****************************************************************************
  812. *
  813. * @doc INTERNAL
  814. *
  815. * @func void | PurgeSingleCallNode |
  816. *
  817. * Purges a single dead node in the call array.
  818. *
  819. * @parm HWND | hwnd |
  820. *
  821. * Window in question.
  822. *
  823. * @parm PSUBCLASS_HEADER | pHeader |
  824. *
  825. * The header associated with the window.
  826. * The <p uCleanup> field is the index of the node being
  827. * cleaned up.
  828. *
  829. *****************************************************************************/
  830. void INTERNAL
  831. PurgeSingleCallNode(HWND hwnd, PSUBCLASS_HEADER pHeader)
  832. {
  833. AssertF(InCrit()); /* we will try to re-arrange the call array */
  834. if (pHeader->uCleanup) {/* Sanity check */
  835. UINT uRemain;
  836. SquirtSqflPtszV(sqfl,
  837. TEXT("PurgeSingleCallNode: Purging number %d"),
  838. pHeader->uCleanup);
  839. /*
  840. * and a little paranoia
  841. */
  842. AssertF(pHeader->CallArray[pHeader->uCleanup].pfnSubclass == 0);
  843. AssertF(fLimpFF(pHeader->pFrameCur,
  844. pHeader->uCleanup < pHeader->pFrameCur->uDeepestCall));
  845. /*
  846. * are there any call nodes above the one we're about to remove?
  847. */
  848. uRemain = pHeader->uRefs - pHeader->uCleanup;
  849. if (uRemain > 0) {
  850. /*
  851. * yup, need to fix up the array the hard way
  852. */
  853. SUBCLASS_CALL *pCall;
  854. SUBCLASS_FRAME *pFrame;
  855. UINT uCur, uMax;
  856. /*
  857. * move the remaining nodes down into the empty space
  858. */
  859. pCall = pHeader->CallArray + pHeader->uCleanup;
  860. /*
  861. * Since the souce and destination overlap (unless there's only
  862. * one node remaining) the behavior of memcpy is undefined.
  863. * memmove (aka MoveMemory) would guarantee the correct
  864. * behavior but requires the runtime library.
  865. * Since this is the only function we require in retail from the
  866. * RTL, it is not worth the 22% bloat we gain from using the
  867. * static version and using the dynamic version is a load time
  868. * and redist test hit. So copy the array one element at a time.
  869. */
  870. for( uCur = 0; uCur < uRemain; uCur++ )
  871. {
  872. memcpy( &pCall[uCur], &pCall[uCur+1], sizeof(*pCall) );
  873. }
  874. /*
  875. * update the call indices of any active frames
  876. */
  877. uCur = pHeader->uCleanup;
  878. pFrame = pHeader->pFrameCur;
  879. while (pFrame) {
  880. if (pFrame->uCallIndex >= uCur) {
  881. pFrame->uCallIndex--;
  882. if (pFrame->uDeepestCall >= uCur) {
  883. pFrame->uDeepestCall--;
  884. }
  885. }
  886. pFrame = pFrame->pFramePrev;
  887. }
  888. /*
  889. * now search for any other dead call nodes in the remaining area
  890. */
  891. uMax = pHeader->uRefs - 1; /* we haven't decremented uRefs yet */
  892. while (uCur < uMax && pCall->pfnSubclass) {
  893. pCall++;
  894. uCur++;
  895. }
  896. pHeader->uCleanup = (uCur < uMax) ? uCur : 0;
  897. } else {
  898. /*
  899. * No call nodes above. This case is easy.
  900. */
  901. pHeader->uCleanup = 0;
  902. }
  903. /*
  904. * finally, decrement the client count
  905. */
  906. pHeader->uRefs--;
  907. SquirtSqflPtszV(sqfl, TEXT("warning: PurgeSingleCallNode: ")
  908. TEXT("Still %d refs"), pHeader->uRefs);
  909. } else {
  910. AssertF(0); /* Nothing to do! */
  911. }
  912. }
  913. /*****************************************************************************
  914. *
  915. * @doc INTERNAL
  916. *
  917. * @func void | CompactSubclassHeader |
  918. *
  919. * Attempts to compact the subclass array, freeing the
  920. * subclass header if the array is empty.
  921. *
  922. * @parm HWND | hwnd |
  923. *
  924. * Window in question.
  925. *
  926. * @parm PSUBCLASS_HEADER | pHeader |
  927. *
  928. * The header associated with the window.
  929. *
  930. *****************************************************************************/
  931. void INTERNAL
  932. CompactSubclassHeader(HWND hwnd, PSUBCLASS_HEADER pHeader)
  933. {
  934. AssertF(InCrit()); /* we will try to re-arrange the call array */
  935. /*
  936. * we must handle the "window destroyed unexpectedly during callback" case
  937. */
  938. if (hwnd) {
  939. /*
  940. * Clean out as many dead callbacks as possible.
  941. *
  942. * The "DeepestCall" test is an optimization so we don't go
  943. * purging call nodes when no active frame cares.
  944. *
  945. * (I'm not entirely conviced of this. I mean, we have to
  946. * purge it eventually anyway, right?)
  947. */
  948. while (pHeader->uCleanup &&
  949. fLimpFF(pHeader->pFrameCur,
  950. pHeader->uCleanup < pHeader->pFrameCur->uDeepestCall)) {
  951. PurgeSingleCallNode(hwnd, pHeader);
  952. }
  953. /*
  954. * do we still have clients?
  955. */
  956. if (pHeader->uRefs > 1) {
  957. SquirtSqflPtszV(sqfl, TEXT("CompactSubclassHeader: ")
  958. TEXT("Still %d users"), pHeader->uRefs);
  959. /*
  960. * yes, shrink our allocation, leaving room for at least one client
  961. */
  962. ReAllocSubclassHeader(hwnd, pHeader, pHeader->uRefs + 1);
  963. goto done;
  964. }
  965. }
  966. /*
  967. * There are no clients left, or the window is gone.
  968. * Try to detach and free
  969. */
  970. DetachSubclassHeader(hwnd, pHeader, FALSE);
  971. done:;
  972. }
  973. /*****************************************************************************
  974. *
  975. * @doc INTERNAL
  976. *
  977. * @func PSUBCLASS_CALL | FindCallRecord |
  978. *
  979. * Searches for a call record with the specified subclass proc
  980. * and id, and returns its address. If no such call record is
  981. * found then NULL is returned.
  982. *
  983. * This is a helper function used when we need to track down
  984. * a callback because the client is changing its refdata or
  985. * removing it.
  986. *
  987. * @parm PSUBCLASS_HEADER | pHeader |
  988. *
  989. * The header in which to search.
  990. *
  991. * @parm SUBCLASSPROC | pfnSubclass |
  992. *
  993. * Subclass callback procedure to locate.
  994. *
  995. * @parm UINT | uIdSubclass |
  996. *
  997. * Instance identifier associated with the callback.
  998. *
  999. *****************************************************************************/
  1000. SUBCLASS_CALL * INTERNAL
  1001. FindCallRecord(PSUBCLASS_HEADER pHeader, SUBCLASSPROC pfnSubclass,
  1002. UINT_PTR uIdSubclass)
  1003. {
  1004. SUBCLASS_CALL *pCall;
  1005. UINT uCallIndex;
  1006. AssertF(InCrit()); /* we'll be scanning the call array */
  1007. /*
  1008. * scan the call array. note that we assume there is always at least
  1009. * one member in the table (our CallOriginalWndProc record)
  1010. */
  1011. uCallIndex = pHeader->uRefs;
  1012. pCall = &pHeader->CallArray[uCallIndex];
  1013. do {
  1014. uCallIndex--;
  1015. pCall--;
  1016. if ((pCall->pfnSubclass == pfnSubclass) &&
  1017. (pCall->uIdSubclass == uIdSubclass))
  1018. {
  1019. return pCall;
  1020. }
  1021. } while (uCallIndex != (UINT)-1);
  1022. return NULL;
  1023. }
  1024. /*****************************************************************************
  1025. *
  1026. * @doc INTERNAL
  1027. *
  1028. * @func BOOL | GetWindowSubclass |
  1029. *
  1030. * Retrieves the reference data for the specified window
  1031. * subclass callback.
  1032. *
  1033. * @parm HWND | hwnd |
  1034. *
  1035. * Window in question.
  1036. *
  1037. * @parm SUBCLASSPROC | pfnSubclass |
  1038. *
  1039. * Subclass callback procedure to locate.
  1040. *
  1041. * @parm UINT | uIdSubclass |
  1042. *
  1043. * Instance identifier associated with the callback.
  1044. *
  1045. * @parm LPDWORD | pdwRefData |
  1046. *
  1047. * Output pointer.
  1048. *
  1049. *****************************************************************************/
  1050. BOOL EXTERNAL
  1051. GetWindowSubclass(HWND hwnd, SUBCLASSPROC pfnSubclass, UINT_PTR uIdSubclass,
  1052. PULONG_PTR pdwRefData)
  1053. {
  1054. BOOL fRc;
  1055. ULONG_PTR dwRefData;
  1056. DllEnterCrit();
  1057. /*
  1058. * sanity
  1059. */
  1060. if (IsWindow(hwnd) && pfnSubclass) {
  1061. PSUBCLASS_HEADER pHeader;
  1062. SUBCLASS_CALL *pCall;
  1063. /*
  1064. * if we've subclassed it and they are a client then get the refdata
  1065. */
  1066. pHeader = GetSubclassHeader(hwnd);
  1067. if (pHeader &&
  1068. (pHeader != (PSUBCLASS_HEADER)1) &&
  1069. (pCall = FindCallRecord(pHeader, pfnSubclass, uIdSubclass)) != 0) {
  1070. /*
  1071. * fetch the refdata and note success
  1072. */
  1073. fRc = 1;
  1074. dwRefData = pCall->dwRefData;
  1075. } else {
  1076. fRc = 0;
  1077. dwRefData = 0;
  1078. }
  1079. } else { /* Invalid window handle */
  1080. // 7/19/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
  1081. SquirtSqflPtszV(sqfl | sqflError, TEXT("GetWindowSubclass: ")
  1082. TEXT("Bad window %p or callback %p"),
  1083. hwnd, pfnSubclass);
  1084. fRc = 0;
  1085. dwRefData = 0;
  1086. }
  1087. /*
  1088. * we always fill in/zero pdwRefData regradless of result
  1089. */
  1090. if (pdwRefData) {
  1091. *pdwRefData = dwRefData;
  1092. }
  1093. DllLeaveCrit();
  1094. return fRc;
  1095. }
  1096. /*****************************************************************************
  1097. *
  1098. * @doc INTERNAL
  1099. *
  1100. * @func BOOL | SetWindowSubclass |
  1101. *
  1102. * Installs/updates a window subclass callback. Subclass
  1103. * callbacks are identified by their callback address and id pair.
  1104. * If the specified callback/id pair is not yet installed then
  1105. * the procedure installs the pair. If the callback/id pair is
  1106. * already installed, then this procedure changes the reference
  1107. * data for the pair.
  1108. *
  1109. * @parm HWND | hwnd |
  1110. *
  1111. * Window in question.
  1112. *
  1113. * @parm SUBCLASSPROC | pfnSubclass |
  1114. *
  1115. * Subclass callback procedure to install or modify.
  1116. *
  1117. * @parm UINT | uIdSubclass |
  1118. *
  1119. * Instance identifier associated with the callback.
  1120. *
  1121. * @parm DWORD | dwRefData |
  1122. *
  1123. * Reference data to associate with the callback/id.
  1124. *
  1125. *****************************************************************************/
  1126. BOOL EXTERNAL
  1127. SetWindowSubclass(HWND hwnd, SUBCLASSPROC pfnSubclass, UINT_PTR uIdSubclass,
  1128. ULONG_PTR dwRefData)
  1129. {
  1130. BOOL fRc;
  1131. /*
  1132. * sanity
  1133. */
  1134. if (IsWindow(hwnd) && pfnSubclass) {
  1135. SUBCLASS_HEADER *pHeader;
  1136. /*
  1137. * we party on the subclass call chain here
  1138. */
  1139. DllEnterCrit();
  1140. /*
  1141. * actually subclass the window
  1142. */
  1143. /*
  1144. * Prefix gets confused (mb:34501) by this. I assume this is because
  1145. * AttachSubclassHeader returns a pointer to allocated memory but we
  1146. * allow the pointer to go out of context without saving it. This is
  1147. * OK because AttachSubclassHeader already saved it for us.
  1148. */
  1149. pHeader = AttachSubclassHeader(hwnd);
  1150. if (pHeader) {
  1151. SUBCLASS_CALL *pCall;
  1152. /*
  1153. * find a call node for this caller
  1154. */
  1155. pCall = FindCallRecord(pHeader, pfnSubclass, uIdSubclass);
  1156. if (pCall == NULL) {
  1157. /*
  1158. * not found, alloc a new one
  1159. */
  1160. SUBCLASS_HEADER *pHeaderT =
  1161. ReAllocSubclassHeader(hwnd, pHeader, pHeader->uRefs + 1);
  1162. if (pHeaderT) {
  1163. pHeader = pHeaderT;
  1164. pCall = &pHeader->CallArray[pHeader->uRefs++];
  1165. } else {
  1166. /*
  1167. * re-query in case it is already gone
  1168. */
  1169. pHeader = FastGetSubclassHeader(hwnd);
  1170. if (pHeader) {
  1171. CompactSubclassHeader(hwnd, pHeader);
  1172. }
  1173. goto bail;
  1174. }
  1175. }
  1176. /*
  1177. * fill in the subclass call data
  1178. */
  1179. pCall->pfnSubclass = pfnSubclass;
  1180. pCall->uIdSubclass = uIdSubclass;
  1181. pCall->dwRefData = dwRefData;
  1182. // 7/19/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
  1183. SquirtSqflPtszV(sqfl,
  1184. TEXT("SetWindowSubclass: Added %p/%d as %d"),
  1185. pfnSubclass, uIdSubclass, pHeader->uRefs - 1);
  1186. fRc = 1;
  1187. } else { /* Unable to subclass */
  1188. bail:;
  1189. fRc = 0;
  1190. }
  1191. DllLeaveCrit();
  1192. } else {
  1193. fRc = 0; /* Invalid parameter */
  1194. }
  1195. return fRc;
  1196. }
  1197. /*****************************************************************************
  1198. *
  1199. * @doc INTERNAL
  1200. *
  1201. * @func BOOL | RemoveWindowSubclass |
  1202. *
  1203. * Removes a subclass callback from a window.
  1204. * Subclass callbacks are identified by their
  1205. * callback address and id pair.
  1206. *
  1207. * @parm HWND | hwnd |
  1208. *
  1209. * Window in question.
  1210. *
  1211. * @parm SUBCLASSPROC | pfnSubclass |
  1212. *
  1213. * Subclass callback procedure to remove.
  1214. *
  1215. * @parm UINT | uIdSubclass |
  1216. *
  1217. * Instance identifier associated with the callback.
  1218. *
  1219. *****************************************************************************/
  1220. BOOL EXTERNAL
  1221. RemoveWindowSubclass(HWND hwnd, SUBCLASSPROC pfnSubclass, UINT_PTR uIdSubclass)
  1222. {
  1223. BOOL fRc;
  1224. /*
  1225. * sanity
  1226. */
  1227. if (IsWindow(hwnd) && pfnSubclass) {
  1228. SUBCLASS_HEADER *pHeader;
  1229. /*
  1230. * we party on the subclass call chain here
  1231. */
  1232. DllEnterCrit();
  1233. /*
  1234. * obtain our subclass data and find the callback to remove.
  1235. */
  1236. pHeader = GetSubclassHeader(hwnd);
  1237. if (pHeader && (pHeader != (PSUBCLASS_HEADER)1) ) {
  1238. SUBCLASS_CALL *pCall;
  1239. /*
  1240. * find the callback to remove
  1241. */
  1242. pCall = FindCallRecord(pHeader, pfnSubclass, uIdSubclass);
  1243. if (pCall) {
  1244. UINT uCall;
  1245. /*
  1246. * disable this node.
  1247. */
  1248. pCall->pfnSubclass = 0;
  1249. /*
  1250. * Remember that we have something to clean up.
  1251. *
  1252. * Set uCleanup to the index of the shallowest node that
  1253. * needs to be cleaned up. CompactSubclassHeader will
  1254. * clean up everything from uCleanup onward.
  1255. */
  1256. uCall = (UINT)(pCall - pHeader->CallArray);
  1257. if (fLimpFF(pHeader->uCleanup, uCall < pHeader->uCleanup)) {
  1258. pHeader->uCleanup = uCall;
  1259. }
  1260. // 7/19/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
  1261. SquirtSqflPtszV(sqfl,
  1262. TEXT("RemoveWindowSubclass: Removing %p/%d as %d"),
  1263. pfnSubclass, uIdSubclass, uCall);
  1264. /*
  1265. * now try to clean up any unused nodes
  1266. */
  1267. CompactSubclassHeader(hwnd, pHeader);
  1268. /*
  1269. * the call above can realloc or free the subclass
  1270. * header for this window, so make sure we don't use it.
  1271. */
  1272. D(pHeader = 0);
  1273. fRc = 1;
  1274. } else { /* Not found */
  1275. fRc = 0;
  1276. }
  1277. } else { /* Never subclassed (ergo not found) */
  1278. fRc = 0;
  1279. }
  1280. /*
  1281. * release the critical section and return the result
  1282. */
  1283. DllLeaveCrit();
  1284. } else {
  1285. fRc = 0; /* Validation failed */
  1286. }
  1287. return fRc;
  1288. }
  1289. /*****************************************************************************
  1290. *
  1291. * @doc INTERNAL
  1292. *
  1293. * @func LRESULT | DefSubclassProc |
  1294. *
  1295. * Calls the next handler in the window's subclass chain.
  1296. * The last handler in the subclass chain is installed by us,
  1297. * and calls the original window procedure for the window.
  1298. *
  1299. * Every subclass procedure should call <f DefSubclassProc>
  1300. * in order to allow the message to be processed by other handlers.
  1301. *
  1302. * @parm HWND | hwnd |
  1303. *
  1304. * Window in question.
  1305. *
  1306. * @parm UINT | wm |
  1307. *
  1308. * Window message that needs to go to the original <t WNDPROC>.
  1309. *
  1310. * @parm WPARAM | wp |
  1311. *
  1312. * Meaning depends on window message.
  1313. *
  1314. * @parm LPARAM | lp |
  1315. *
  1316. * Meaning depends on window message.
  1317. *
  1318. *****************************************************************************/
  1319. LRESULT EXTERNAL
  1320. DefSubclassProc(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp)
  1321. {
  1322. LRESULT lResult;
  1323. /*
  1324. * make sure the window is still valid
  1325. */
  1326. if (IsWindow(hwnd)) {
  1327. PSUBCLASS_HEADER pHeader;
  1328. /*
  1329. * take the critical section while we figure out who to call next
  1330. */
  1331. AssertF(!InCrit());
  1332. DllEnterCrit();
  1333. /*
  1334. * complain if we are being called improperly
  1335. */
  1336. pHeader = FastGetSubclassHeader(hwnd);
  1337. if (pHeader &&
  1338. pHeader->pFrameCur &&
  1339. GetCurrentThreadId() == pHeader->dwThreadId) {
  1340. /*
  1341. * call the next proc in the subclass chain
  1342. *
  1343. * WARNING: this call temporarily releases the critical section
  1344. * WARNING: pHeader is invalid when this call returns
  1345. */
  1346. lResult = CallNextSubclassProc(pHeader, hwnd, wm, wp, lp);
  1347. D(pHeader = 0);
  1348. } else {
  1349. SquirtSqflPtszV(sqfl | sqflError,
  1350. TEXT("DefSubclassProc: Called improperly"));
  1351. lResult = 0;
  1352. }
  1353. DllLeaveCrit();
  1354. } else {
  1355. // 7/19/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
  1356. SquirtSqflPtszV(sqfl | sqflError,
  1357. TEXT("DefSubclassProc: %P not a window"),
  1358. hwnd);
  1359. lResult = 0;
  1360. }
  1361. return lResult;
  1362. }
  1363. /*****************************************************************************
  1364. *
  1365. * @doc INTERNAL
  1366. *
  1367. * @func void | UpdateDeepestCall |
  1368. *
  1369. * Updates the deepest call index for the specified frame.
  1370. *
  1371. * @parm PSUBCLASS_FRAME | pFrame |
  1372. *
  1373. * Frame in question.
  1374. *
  1375. *****************************************************************************/
  1376. void INTERNAL
  1377. UpdateDeepestCall(SUBCLASS_FRAME *pFrame)
  1378. {
  1379. AssertF(InCrit()); /* we are partying on the frame list */
  1380. /*
  1381. * My deepest call equals my current call or my parent's
  1382. * deepest call, whichever is deeper.
  1383. */
  1384. if (pFrame->pFramePrev &&
  1385. (pFrame->pFramePrev->uDeepestCall < pFrame->uCallIndex)) {
  1386. pFrame->uDeepestCall = pFrame->pFramePrev->uDeepestCall;
  1387. } else {
  1388. pFrame->uDeepestCall = pFrame->uCallIndex;
  1389. }
  1390. }
  1391. /*****************************************************************************
  1392. *
  1393. * @doc INTERNAL
  1394. *
  1395. * @func void | EnterSubclassFrame |
  1396. *
  1397. * Sets up a new subclass frame for the specified header,
  1398. * saving away the previous one.
  1399. *
  1400. * @parm PSUBCLASS_HEADER | pHeader |
  1401. *
  1402. * Header in question.
  1403. *
  1404. * @parm PSUBCLASS_FRAME | pFrame |
  1405. *
  1406. * Brand new frame to link in.
  1407. *
  1408. *****************************************************************************/
  1409. void INLINE
  1410. EnterSubclassFrame(PSUBCLASS_HEADER pHeader, SUBCLASS_FRAME *pFrame)
  1411. {
  1412. AssertF(InCrit()); /* we are partying on the header and frame list */
  1413. /*
  1414. * fill in the frame and link it into the header
  1415. */
  1416. pFrame->uCallIndex = pHeader->uRefs + 1;
  1417. pFrame->pFramePrev = pHeader->pFrameCur;
  1418. pFrame->pHeader = pHeader;
  1419. pHeader->pFrameCur = pFrame;
  1420. /*
  1421. * initialize the deepest call index for this frame
  1422. */
  1423. UpdateDeepestCall(pFrame);
  1424. }
  1425. /*****************************************************************************
  1426. *
  1427. * @doc INTERNAL
  1428. *
  1429. * @func void | LeaveSubclassFrame |
  1430. *
  1431. * Tear down the current subclass frame, restoring the previous one.
  1432. *
  1433. * @parm PSUBCLASS_FRAME | pFrame |
  1434. *
  1435. * Frame going away.
  1436. *
  1437. *****************************************************************************/
  1438. PSUBCLASS_HEADER INLINE
  1439. LeaveSubclassFrame(SUBCLASS_FRAME *pFrame)
  1440. {
  1441. PSUBCLASS_HEADER pHeader;
  1442. AssertF(InCrit()); /* we are partying on the header */
  1443. /*
  1444. * unlink the frame from its header (if it still exists)
  1445. */
  1446. pHeader = pFrame->pHeader;
  1447. if (pHeader) {
  1448. pHeader->pFrameCur = pFrame->pFramePrev;
  1449. }
  1450. return pHeader;
  1451. }
  1452. #ifdef SUBCLASS_HANDLEEXCEPTIONS
  1453. /*****************************************************************************
  1454. *
  1455. * @doc INTERNAL
  1456. *
  1457. * @func void | SubclassFrameException |
  1458. *
  1459. * Clean up when a exception is thrown from a subclass frame.
  1460. *
  1461. * @parm PSUBCLASS_FRAME | pFrame |
  1462. *
  1463. * Frame to clean up.
  1464. *
  1465. *****************************************************************************/
  1466. void INTERNAL
  1467. SubclassFrameException(SUBCLASS_FRAME *pFrame)
  1468. {
  1469. /*
  1470. * clean up the current subclass frame
  1471. */
  1472. AssertF(!InCrit());
  1473. DllEnterCrit();
  1474. SquirtSqflPtszV(sqfl | sqflError, TEXT("SubclassFrameException: ")
  1475. TEXT("cleaning up subclass frame after exception"));
  1476. LeaveSubclassFrame(pFrame);
  1477. DllLeaveCrit();
  1478. }
  1479. #endif
  1480. /*****************************************************************************
  1481. *
  1482. * @doc INTERNAL
  1483. *
  1484. * @func LRESULT | MasterSubclassProc |
  1485. *
  1486. * The window procedure we install to dispatch subclass
  1487. * callbacks.
  1488. *
  1489. * It maintains a linked list of "frames" through the stack
  1490. * which allow <f DefSubclassProc> to call the right subclass
  1491. * procedure in multiple-message scenarios.
  1492. *
  1493. * @parm HWND | hwnd |
  1494. *
  1495. * Window under attack.
  1496. *
  1497. * @parm UINT | wm |
  1498. *
  1499. * Window message.
  1500. *
  1501. * @parm WPARAM | wp |
  1502. *
  1503. * Meaning depends on window message.
  1504. *
  1505. * @parm LPARAM | lp |
  1506. *
  1507. * Meaning depends on window message.
  1508. *
  1509. *****************************************************************************/
  1510. LRESULT CALLBACK
  1511. MasterSubclassProc(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp)
  1512. {
  1513. SUBCLASS_HEADER *pHeader;
  1514. LRESULT lResult;
  1515. /*
  1516. * prevent people from partying on the callback chain while we look at it
  1517. */
  1518. AssertF(!InCrit());
  1519. DllEnterCrit();
  1520. /*
  1521. * We'd better have our data.
  1522. */
  1523. pHeader = FastGetSubclassHeader(hwnd);
  1524. if (pHeader) {
  1525. SUBCLASS_FRAME Frame;
  1526. /*
  1527. * set up a new subclass frame and save away the previous one
  1528. */
  1529. EnterSubclassFrame(pHeader, &Frame);
  1530. #ifdef SUBCLASS_HANDLEEXCEPTIONS
  1531. __try /* protect our state information from exceptions */
  1532. #endif
  1533. {
  1534. /*
  1535. * go ahead and call the subclass chain on this frame
  1536. *
  1537. * WARNING: this call temporarily releases the critical section
  1538. * WARNING: pHeader is invalid when this call returns
  1539. */
  1540. lResult =
  1541. CallNextSubclassProc(pHeader, hwnd, wm, wp, lp);
  1542. D(pHeader = 0);
  1543. }
  1544. #ifdef SUBCLASS_HANDLEEXCEPTIONS
  1545. __except (SubclassFrameException(&Frame), EXCEPTION_CONTINUE_SEARCH)
  1546. {
  1547. AssertF(0);
  1548. }
  1549. #endif
  1550. AssertF(InCrit());
  1551. /*
  1552. * restore the previous subclass frame
  1553. */
  1554. pHeader = LeaveSubclassFrame(&Frame);
  1555. /*
  1556. * Do postprocessing if the header is still here.
  1557. */
  1558. if (pHeader) {
  1559. /*
  1560. * was the window nuked (somehow)
  1561. * without us seeing the WM_NCDESTROY?
  1562. */
  1563. if (!IsWindow(hwnd)) {
  1564. /*
  1565. * EVIL! somebody subclassed after us and didn't pass on WM_NCDESTROY
  1566. */
  1567. AssertF(!TEXT("unknown subclass proc swallowed a WM_NCDESTROY"));
  1568. /* go ahead and clean up now */
  1569. hwnd = 0;
  1570. wm = WM_NCDESTROY;
  1571. }
  1572. /*
  1573. * if we are returning from a WM_NCDESTROY then we need to clean up
  1574. */
  1575. if (wm == WM_NCDESTROY) {
  1576. DetachSubclassHeader(hwnd, pHeader, TRUE);
  1577. } else {
  1578. /*
  1579. * is there any pending cleanup, or are all our clients gone?
  1580. */
  1581. if (pHeader->uCleanup ||
  1582. (!pHeader->pFrameCur && (pHeader->uRefs <= 1))) {
  1583. CompactSubclassHeader(hwnd, pHeader);
  1584. D(pHeader = 0);
  1585. }
  1586. }
  1587. /*
  1588. * all done
  1589. */
  1590. } else {
  1591. /*
  1592. * Header is gone. We already cleaned up in a nested frame.
  1593. */
  1594. }
  1595. DllLeaveCrit();
  1596. AssertF(!InCrit());
  1597. } else { /* Our property vanished! */
  1598. DllLeaveCrit();
  1599. lResult = SubclassDeath(hwnd, wm, wp, lp);
  1600. }
  1601. return lResult;
  1602. }
  1603. /*****************************************************************************
  1604. *
  1605. * @doc INTERNAL
  1606. *
  1607. * @func UINT | EnterSubclassCallback |
  1608. *
  1609. * Finds the next callback in the subclass chain and updates
  1610. * <p pFrame> to indicate that we are calling it.
  1611. *
  1612. * @parm PSUBCLASS_HEADER | pHeader |
  1613. *
  1614. * Controlling header.
  1615. *
  1616. * @parm PSUBCLASS_FRAME | pFrame |
  1617. *
  1618. * Frame to update.
  1619. *
  1620. * @parm SUBCLASS_CALL * | pCallChosen |
  1621. *
  1622. * The call selected for calling.
  1623. *
  1624. *****************************************************************************/
  1625. UINT INTERNAL
  1626. EnterSubclassCallback(PSUBCLASS_HEADER pHeader, SUBCLASS_FRAME *pFrame,
  1627. SUBCLASS_CALL *pCallChosen)
  1628. {
  1629. SUBCLASS_CALL *pCall;
  1630. UINT uDepth;
  1631. /*
  1632. * we will be scanning the subclass chain and updating frame data
  1633. */
  1634. AssertF(InCrit());
  1635. /*
  1636. * scan the subclass chain for the next callable subclass callback
  1637. * Assert that the loop will terminate.
  1638. */
  1639. AssertF(pHeader->CallArray[0].pfnSubclass);
  1640. pCall = pHeader->CallArray + pFrame->uCallIndex;
  1641. uDepth = 0;
  1642. do {
  1643. uDepth++;
  1644. pCall--;
  1645. } while (!pCall->pfnSubclass);
  1646. /*
  1647. * copy the callback information for the caller
  1648. */
  1649. pCallChosen->pfnSubclass = pCall->pfnSubclass;
  1650. pCallChosen->uIdSubclass = pCall->uIdSubclass;
  1651. pCallChosen->dwRefData = pCall->dwRefData;
  1652. /*
  1653. * adjust the frame's call index by the depth we entered
  1654. */
  1655. pFrame->uCallIndex -= uDepth;
  1656. /*
  1657. * keep the deepest call index up to date
  1658. */
  1659. UpdateDeepestCall(pFrame);
  1660. return uDepth;
  1661. }
  1662. /*****************************************************************************
  1663. *
  1664. * @doc INTERNAL
  1665. *
  1666. * @func void | LeaveSubclassCallback |
  1667. *
  1668. * Get out one level.
  1669. *
  1670. * @parm PSUBCLASS_FRAME | pFrame |
  1671. *
  1672. * Frame to update.
  1673. *
  1674. * @parm UINT | uDepth |
  1675. *
  1676. * Who just finished.
  1677. *
  1678. *****************************************************************************/
  1679. void INLINE
  1680. LeaveSubclassCallback(SUBCLASS_FRAME *pFrame, UINT uDepth)
  1681. {
  1682. /*
  1683. * we will be updating subclass frame data
  1684. */
  1685. AssertF(InCrit());
  1686. /*
  1687. * adjust the frame's call index by the depth we entered and return
  1688. */
  1689. pFrame->uCallIndex += uDepth;
  1690. /*
  1691. * keep the deepest call index up to date
  1692. */
  1693. UpdateDeepestCall(pFrame);
  1694. }
  1695. #ifdef SUBCLASS_HANDLEEXCEPTIONS
  1696. /*****************************************************************************
  1697. *
  1698. * @doc INTERNAL
  1699. *
  1700. * @func void | SubclassCallbackException |
  1701. *
  1702. * Clean up when a subclass callback throws an exception.
  1703. *
  1704. * @parm PSUBCLASS_FRAME | pFrame |
  1705. *
  1706. * Frame to clean up.
  1707. *
  1708. * @parm UINT | uDepth |
  1709. *
  1710. * Where we were.
  1711. *
  1712. *****************************************************************************/
  1713. void INTERNAL
  1714. SubclassCallbackException(SUBCLASS_FRAME *pFrame, UINT uDepth)
  1715. {
  1716. /*
  1717. * clean up the current subclass callback
  1718. */
  1719. AssertF(!InCrit());
  1720. DllEnterCrit();
  1721. SquirtSqflPtszV(sqfl | sqflError, TEXT("SubclassCallbackException: ")
  1722. TEXT("cleaning up subclass callback after exception"));
  1723. LeaveSubclassCallback(pFrame, uDepth);
  1724. DllLeaveCrit();
  1725. }
  1726. #endif
  1727. /*****************************************************************************
  1728. *
  1729. * @doc INTERNAL
  1730. *
  1731. * @func LRESULT | CallNextSubclassProc |
  1732. *
  1733. * Calls the next subclass callback in the subclass chain.
  1734. *
  1735. * WARNING: this call temporarily releases the critical section.
  1736. *
  1737. * WARNING: <p pHeader> is invalid when this call returns.
  1738. *
  1739. * @parm PSUBCLASS_HEADER | pHeader |
  1740. *
  1741. * The header that is tracking the state.
  1742. *
  1743. * @parm HWND | hwnd |
  1744. *
  1745. * Window under attack.
  1746. *
  1747. * @parm UINT | wm |
  1748. *
  1749. * Window message.
  1750. *
  1751. * @parm WPARAM | wp |
  1752. *
  1753. * Meaning depends on window message.
  1754. *
  1755. * @parm LPARAM | lp |
  1756. *
  1757. * Meaning depends on window message.
  1758. *
  1759. *****************************************************************************/
  1760. LRESULT INTERNAL
  1761. CallNextSubclassProc(PSUBCLASS_HEADER pHeader, HWND hwnd, UINT wm,
  1762. WPARAM wp, LPARAM lp)
  1763. {
  1764. SUBCLASS_CALL Call;
  1765. SUBCLASS_FRAME *pFrame;
  1766. LRESULT lResult;
  1767. UINT uDepth;
  1768. AssertF(InCrit()); /* sanity */
  1769. AssertF(pHeader); /* paranoia */
  1770. /*
  1771. * get the current subclass frame
  1772. */
  1773. pFrame = pHeader->pFrameCur;
  1774. AssertF(pFrame);
  1775. /*
  1776. * get the next subclass call we need to make
  1777. */
  1778. uDepth = EnterSubclassCallback(pHeader, pFrame, &Call);
  1779. /*
  1780. * leave the critical section so we don't deadlock in our callback
  1781. *
  1782. * WARNING: pHeader is invalid when this call returns
  1783. */
  1784. DllLeaveCrit();
  1785. D(pHeader = 0);
  1786. /*
  1787. * we call the outside world so prepare to deadlock if we have the critsec
  1788. */
  1789. AssertF(!InCrit());
  1790. #ifdef SUBCLASS_HANDLEEXCEPTIONS
  1791. __try /* protect our state information from exceptions */
  1792. #endif
  1793. {
  1794. /*
  1795. * call the chosen subclass proc
  1796. */
  1797. AssertF(Call.pfnSubclass);
  1798. lResult = Call.pfnSubclass(hwnd, wm, wp, lp,
  1799. Call.uIdSubclass, Call.dwRefData);
  1800. }
  1801. #ifdef SUBCLASS_HANDLEEXCEPTIONS
  1802. __except (SubclassCallbackException(pFrame, uDepth),
  1803. EXCEPTION_CONTINUE_SEARCH)
  1804. {
  1805. AssertF(0);
  1806. }
  1807. #endif
  1808. /*
  1809. * we left the critical section before calling out so re-enter it
  1810. */
  1811. AssertF(!InCrit());
  1812. DllEnterCrit();
  1813. /*
  1814. * finally, clean up and return
  1815. */
  1816. LeaveSubclassCallback(pFrame, uDepth);
  1817. return lResult;
  1818. }