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.

933 lines
25 KiB

  1. #include "stdafx.h"
  2. #include "Lava.h"
  3. #include "Spy.h"
  4. #include "..\DUser\resource.h"
  5. #if DBG
  6. const UINT IDC_GADGETTREE = 1;
  7. const int cxValue = 80;
  8. const int cxBorder = 5;
  9. const int cyBorder = 5;
  10. /***************************************************************************\
  11. *****************************************************************************
  12. *
  13. * class Spy
  14. *
  15. *****************************************************************************
  16. \***************************************************************************/
  17. PRID Spy::s_pridLink = 0;
  18. ATOM Spy::s_atom = NULL;
  19. HFONT Spy::s_hfntDesc = NULL;
  20. HFONT Spy::s_hfntDescBold = NULL;
  21. HBRUSH Spy::s_hbrOutline = NULL;
  22. int Spy::s_cyLinePxl = 0;
  23. DWORD Spy::g_tlsSpy = (DWORD) -1;
  24. CritLock Spy::s_lockList;
  25. GList<Spy> Spy::s_lstSpys;
  26. static const GUID guidLink = { 0xd5818900, 0xaf18, 0x4c98, { 0x87, 0x20, 0x5a, 0x32, 0x47, 0xa3, 0x1, 0x78 } }; // {D5818900-AF18-4c98-8720-5A3247A30178}
  27. //------------------------------------------------------------------------------
  28. Spy::Spy()
  29. {
  30. }
  31. //------------------------------------------------------------------------------
  32. Spy::~Spy()
  33. {
  34. s_lockList.Enter();
  35. s_lstSpys.Unlink(this);
  36. s_lockList.Leave();
  37. }
  38. //------------------------------------------------------------------------------
  39. BOOL
  40. Spy::BuildSpy(HWND hwndParent, HGADGET hgadRoot, HGADGET hgadSelect)
  41. {
  42. BOOL fSuccess = FALSE;
  43. Spy * pSpy, * pSpyCur;
  44. s_lockList.Enter();
  45. //
  46. // Perform first-time initialization for Spy
  47. //
  48. if (g_tlsSpy == -1) {
  49. //
  50. // Allocate a TLS slot for Spy. This is DEBUG only, so we don't worry about
  51. // the extra cost. However, if this ever becomes on in RETAIL, we need to
  52. // create a SubTread for Lava and add a Spy slot.
  53. //
  54. g_tlsSpy = TlsAlloc();
  55. if (g_tlsSpy == -1) {
  56. goto Exit;
  57. }
  58. //
  59. // Initialize CommCtrl.
  60. //
  61. INITCOMMONCONTROLSEX icc;
  62. icc.dwSize = sizeof(icc);
  63. icc.dwICC = ICC_TREEVIEW_CLASSES;
  64. if (!InitCommonControlsEx(&icc)) {
  65. goto Exit;
  66. }
  67. }
  68. AssertMsg(::GetGadget(hgadRoot, GG_PARENT) == NULL, "Ensure Root Gadget");
  69. //
  70. // Each Gadget subtree can only be spied on once because there are
  71. // back-pointers from each Gadget to the corresponding HTREEITEM's. Need to
  72. // check if this Gadget subtree is already is being spied on.
  73. //
  74. pSpyCur = s_lstSpys.GetHead();
  75. while (pSpyCur != NULL) {
  76. if (pSpyCur->m_hgadRoot == hgadRoot) {
  77. //
  78. // Already exists, so don't open another Spy.
  79. //
  80. SetForegroundWindow(pSpyCur->m_hwnd);
  81. goto Exit;
  82. }
  83. pSpyCur = pSpyCur->GetNext();
  84. }
  85. //
  86. // Register a WNDCLASS to use
  87. //
  88. if (s_atom == NULL) {
  89. WNDCLASSEX wcex;
  90. ZeroMemory(&wcex, sizeof(wcex));
  91. wcex.cbSize = sizeof(WNDCLASSEX);
  92. wcex.style = CS_HREDRAW | CS_VREDRAW;
  93. wcex.lpfnWndProc = RawSpyWndProc;
  94. wcex.hInstance = g_hDll;
  95. wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
  96. wcex.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1);
  97. wcex.lpszClassName = "GadgetSpy (Inside)";
  98. s_atom = RegisterClassEx(&wcex);
  99. if (s_atom == NULL) {
  100. goto Exit;
  101. }
  102. }
  103. //
  104. // Create GDI objects used in painting
  105. //
  106. if (s_hfntDesc == NULL) {
  107. s_hfntDesc = UtilBuildFont(L"Tahoma", 85, FS_NORMAL, NULL);
  108. HDC hdc = GetGdiCache()->GetCompatibleDC();
  109. HFONT hfntOld = (HFONT) SelectObject(hdc, s_hfntDesc);
  110. TEXTMETRIC tm;
  111. GetTextMetrics(hdc, &tm);
  112. s_cyLinePxl = tm.tmHeight;
  113. SelectObject(hdc, hfntOld);
  114. GetGdiCache()->ReleaseCompatibleDC(hdc);
  115. }
  116. if (s_hfntDescBold == NULL) {
  117. s_hfntDescBold = UtilBuildFont(L"Tahoma", 85, FS_BOLD, NULL);
  118. }
  119. if (s_hbrOutline == NULL) {
  120. s_hbrOutline = CreateSolidBrush(GetSysColor(COLOR_3DSHADOW));
  121. }
  122. if (s_pridLink == 0) {
  123. s_pridLink = RegisterGadgetProperty(&guidLink);
  124. }
  125. //
  126. // Create a new Spy instance and HWND
  127. //
  128. pSpy = ProcessNew(Spy);
  129. if (pSpy == NULL) {
  130. goto Exit;
  131. }
  132. pSpy->m_hgadMsg = CreateGadget(NULL, GC_MESSAGE, RawEventProc, pSpy);
  133. pSpy->m_hgadRoot = hgadRoot;
  134. Verify(TlsSetValue(g_tlsSpy, pSpy));
  135. {
  136. RECT rcParentPxl;
  137. GetWindowRect(hwndParent, &rcParentPxl);
  138. HWND hwndSpy = CreateWindowEx(WS_EX_TOOLWINDOW | WS_EX_CLIENTEDGE,
  139. (LPCTSTR) s_atom, NULL,
  140. WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, rcParentPxl.left + 20, rcParentPxl.top + 20,
  141. 300, 500, hwndParent, NULL, g_hDll, NULL);
  142. if (hwndSpy == NULL) {
  143. ProcessDelete(Spy, pSpy);
  144. goto Exit;
  145. }
  146. pSpy->UpdateTitle();
  147. ShowWindow(hwndSpy, SW_SHOW);
  148. UpdateWindow(hwndSpy);
  149. }
  150. //
  151. // Select the specified Gadget as a starting point. We want to check
  152. // if this HGADGET is actually a valid child since it may have been
  153. // "grabbed" at an earlier time and no longer be valid.
  154. //
  155. if (hgadSelect) {
  156. CheckIsChildData cicd;
  157. cicd.hgadCheck = hgadSelect;
  158. cicd.fChild = FALSE;
  159. Verify(EnumGadgets(hgadRoot, EnumCheckIsChild, &cicd, GENUM_DEEPCHILD));
  160. if (cicd.fChild) {
  161. HTREEITEM htiSelect;
  162. if (GetGadgetProperty(hgadSelect, s_pridLink, (void **) &htiSelect)) {
  163. AssertMsg(htiSelect != NULL, "Must have valid HTREEITEM");
  164. if (!TreeView_SelectItem(pSpy->m_hwndTree, htiSelect)) {
  165. Trace("SPY: Unable to select default Gadget\n");
  166. }
  167. }
  168. }
  169. }
  170. s_lstSpys.Add(pSpy);
  171. fSuccess = TRUE;
  172. Exit:
  173. s_lockList.Leave();
  174. return fSuccess;
  175. }
  176. //------------------------------------------------------------------------------
  177. BOOL CALLBACK
  178. Spy::EnumCheckIsChild(HGADGET hgad, void * pvData)
  179. {
  180. CheckIsChildData * pcicd = (CheckIsChildData *) pvData;
  181. if (hgad == pcicd->hgadCheck) {
  182. pcicd->fChild = TRUE;
  183. return FALSE; // No longer need to enumerate
  184. }
  185. return TRUE;
  186. }
  187. //------------------------------------------------------------------------------
  188. void
  189. Spy::UpdateTitle()
  190. {
  191. TCHAR szTitle[256];
  192. wsprintf(szTitle, "Root HGADGET = 0x%p, %d Gadgets", m_hgadRoot, m_cItems);
  193. SetWindowText(m_hwnd, szTitle);
  194. }
  195. //------------------------------------------------------------------------------
  196. LRESULT CALLBACK
  197. Spy::RawSpyWndProc(HWND hwnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
  198. {
  199. Spy * pSpy = (Spy *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
  200. if (pSpy == NULL) {
  201. //
  202. // Creating a new Spy HWND, so hook up to the Spy object that was
  203. // previously created.
  204. //
  205. pSpy = reinterpret_cast<Spy *> (TlsGetValue(g_tlsSpy));
  206. AssertMsg(pSpy != NULL, "Ensure already created new Spy instance");
  207. TlsSetValue(g_tlsSpy, NULL);
  208. pSpy->m_hwnd = hwnd;
  209. SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) pSpy);
  210. }
  211. AssertMsg(pSpy->m_hwnd == hwnd, "Ensure HWND's match");
  212. return pSpy->SpyWndProc(nMsg, wParam, lParam);
  213. }
  214. //------------------------------------------------------------------------------
  215. LRESULT
  216. Spy::SpyWndProc(UINT nMsg, WPARAM wParam, LPARAM lParam)
  217. {
  218. switch (nMsg)
  219. {
  220. case WM_CREATE:
  221. if (DefWindowProc(m_hwnd, nMsg, wParam, lParam) == -1) {
  222. return -1;
  223. }
  224. //
  225. // Setup the window
  226. //
  227. AssertMsg(m_hgadRoot != NULL, "Must already have specified Gadget");
  228. RECT rcClient;
  229. GetClientRect(m_hwnd, &rcClient);
  230. m_hwndTree = CreateWindowEx(0, _T("SysTreeView32"),
  231. NULL, WS_CHILD | WS_VISIBLE | TVS_HASBUTTONS | TVS_HASLINES | TVS_SHOWSELALWAYS,
  232. 0, 0, rcClient.right, rcClient.bottom, m_hwnd, (HMENU)((UINT_PTR)IDC_GADGETTREE), g_hDll, NULL);
  233. m_hilc = ImageList_LoadImage(g_hDll, MAKEINTRESOURCE(IDB_SPYICON),
  234. 16, 1, RGB(128, 0, 128), IMAGE_BITMAP, LR_SHARED);
  235. if ((m_hwndTree == NULL) || (m_hilc == NULL)) {
  236. return -1;
  237. }
  238. TreeView_SetImageList(m_hwndTree, m_hilc, TVSIL_NORMAL);
  239. EnumData ed;
  240. ed.pspy = this;
  241. ed.htiParent = InsertTreeItem(TVI_ROOT, m_hgadRoot);
  242. ed.nLevel = 1;
  243. Verify(EnumGadgets(m_hgadRoot, EnumAddList, &ed, GENUM_SHALLOWCHILD));
  244. m_fValid = TRUE;
  245. TreeView_Expand(m_hwndTree, ed.htiParent, TVE_EXPAND);
  246. m_hgadDetails = m_hgadRoot;
  247. UpdateDetails();
  248. break;
  249. case WM_DESTROY:
  250. SelectGadget(NULL);
  251. ::DeleteHandle(m_hgadMsg);
  252. break;
  253. case WM_NCDESTROY:
  254. ProcessDelete(Spy, this);
  255. goto CallDWP;
  256. case WM_SIZE:
  257. if ((wParam == SIZE_MAXIMIZED) || (wParam == SIZE_RESTORED)) {
  258. UpdateLayout();
  259. }
  260. break;
  261. case WM_PAINT:
  262. {
  263. PAINTSTRUCT ps;
  264. HDC hdc = BeginPaint(m_hwnd, &ps);
  265. OnPaint(hdc);
  266. EndPaint(m_hwnd, &ps);
  267. }
  268. break;
  269. case WM_NOTIFY:
  270. if (wParam == IDC_GADGETTREE) {
  271. NMHDR * pnm = (NMHDR *) lParam;
  272. if (pnm->code == TVN_SELCHANGED) {
  273. NMTREEVIEW * pnmtv = (NMTREEVIEW *) lParam;
  274. if (m_fValid) {
  275. SelectGadget(GetGadget(pnmtv->itemNew.hItem));
  276. }
  277. break;
  278. } else if (pnm->code == TVN_KEYDOWN) {
  279. NMTVKEYDOWN * pnmtvkd = (NMTVKEYDOWN *) lParam;
  280. if (pnmtvkd->wVKey == VK_APPS) {
  281. DisplayContextMenu(TRUE);
  282. }
  283. } else if (pnm->code == NM_RCLICK) {
  284. DisplayContextMenu(FALSE);
  285. }
  286. }
  287. goto CallDWP;
  288. default:
  289. CallDWP:
  290. return DefWindowProc(m_hwnd, nMsg, wParam, lParam);
  291. }
  292. return 0;
  293. }
  294. //------------------------------------------------------------------------------
  295. HRESULT CALLBACK
  296. Spy::RawEventProc(HGADGET hgadCur, void * pvCur, EventMsg * pmsg)
  297. {
  298. UNREFERENCED_PARAMETER(hgadCur);
  299. Spy * pSpy = (Spy *) pvCur;
  300. return pSpy->EventProc(pmsg);
  301. }
  302. //------------------------------------------------------------------------------
  303. BOOL
  304. IsDescendant(
  305. HGADGET hgadParent,
  306. HGADGET hgadChild)
  307. {
  308. AssertMsg(hgadParent != NULL, "Must have valid parent");
  309. if (hgadChild == hgadParent) {
  310. return TRUE;
  311. } if (hgadChild == NULL) {
  312. return FALSE;
  313. } else {
  314. return IsDescendant(hgadParent, ::GetGadget(hgadChild, GG_PARENT));
  315. }
  316. }
  317. //------------------------------------------------------------------------------
  318. HRESULT
  319. Spy::EventProc(EventMsg * pmsg)
  320. {
  321. switch (GET_EVENT_DEST(pmsg))
  322. {
  323. case GMF_DIRECT:
  324. //
  325. // Our Listener is being destroyed. We need to detach from everything.
  326. //
  327. if (m_hgadRoot != NULL) {
  328. Trace("SPY: Destroying Spy MsgGadget\n");
  329. Verify(EnumGadgets(m_hgadRoot, EnumRemoveLink, NULL, GENUM_DEEPCHILD));
  330. m_hgadRoot = NULL;
  331. }
  332. break;
  333. case GMF_EVENT:
  334. switch (pmsg->nMsg)
  335. {
  336. case GM_DESTROY:
  337. {
  338. GMSG_DESTROY * pmsgD = (GMSG_DESTROY *) pmsg;
  339. if (pmsgD->nCode == GDESTROY_START) {
  340. //
  341. // Gadget is being destroyed
  342. //
  343. Trace("SPY: Destroying Gadget 0x%p\n", pmsg->hgadMsg);
  344. HTREEITEM hti;
  345. if (GetGadgetProperty(pmsg->hgadMsg, s_pridLink, (void **) &hti)) {
  346. AssertMsg(hti != NULL, "Must have valid HTREEITEM");
  347. Verify(EnumGadgets(pmsg->hgadMsg, EnumRemoveLink, NULL, GENUM_DEEPCHILD));
  348. TreeView_DeleteItem(m_hwndTree, hti);
  349. }
  350. }
  351. }
  352. break;
  353. case GM_CHANGERECT:
  354. if (IsDescendant(pmsg->hgadMsg, m_hgadDetails)) {
  355. UpdateDetails();
  356. }
  357. break;
  358. }
  359. }
  360. return DU_S_NOTHANDLED;
  361. }
  362. //------------------------------------------------------------------------------
  363. BOOL CALLBACK
  364. Spy::EnumAddList(HGADGET hgad, void * pvData)
  365. {
  366. EnumData * ped = (EnumData *) pvData;
  367. Spy * pSpy = ped->pspy;
  368. HTREEITEM htiNew = pSpy->InsertTreeItem(ped->htiParent, hgad);
  369. pSpy->m_cItems++;
  370. if (::GetGadget(hgad, GG_TOPCHILD) != NULL) {
  371. EnumData ed;
  372. ed.pspy = pSpy;
  373. ed.htiParent = htiNew;
  374. ed.nLevel = ped->nLevel + 1;
  375. Verify(EnumGadgets(hgad, EnumAddList, &ed, GENUM_SHALLOWCHILD));
  376. if (ped->nLevel <= 2) {
  377. TreeView_Expand(pSpy->m_hwndTree, htiNew, TVE_EXPAND);
  378. }
  379. }
  380. return TRUE;
  381. }
  382. //------------------------------------------------------------------------------
  383. BOOL CALLBACK
  384. Spy::EnumRemoveLink(HGADGET hgad, void * pvData)
  385. {
  386. UNREFERENCED_PARAMETER(pvData);
  387. RemoveGadgetProperty(hgad, s_pridLink);
  388. return TRUE;
  389. }
  390. //------------------------------------------------------------------------------
  391. void
  392. Spy::SelectGadget(HGADGET hgad)
  393. {
  394. m_hgadDetails = hgad;
  395. {
  396. //
  397. // We are bypassinging the normal API's to directly call a
  398. // DEBUG-only function. Need to lock the Context and do FULL handle
  399. // validation.
  400. //
  401. ContextLock cl;
  402. if (!cl.LockNL(ContextLock::edDefer)) {
  403. return;
  404. }
  405. DuVisual * pgadTree = ValidateVisual(hgad);
  406. DuVisual::DEBUG_SetOutline(pgadTree);
  407. }
  408. UpdateDetails();
  409. }
  410. //------------------------------------------------------------------------------
  411. HTREEITEM
  412. Spy::InsertTreeItem(HTREEITEM htiParent, HGADGET hgad)
  413. {
  414. TCHAR szName[1024];
  415. GMSG_QUERYDESC msg;
  416. msg.cbSize = sizeof(msg);
  417. msg.hgadMsg = hgad;
  418. msg.nMsg = GM_QUERY;
  419. msg.nCode = GQUERY_DESCRIPTION;
  420. msg.szName[0] = '\0';
  421. msg.szType[0] = '\0';
  422. if (DUserSendEvent(&msg, 0) == DU_S_COMPLETE) {
  423. if (msg.szName[0] != '\0') {
  424. wsprintf(szName, "0x%p %S: \"%S\"", hgad, msg.szType, msg.szName);
  425. } else {
  426. wsprintf(szName, "0x%p %S", hgad, msg.szType);
  427. }
  428. } else {
  429. wsprintf(szName, "0x%p", hgad);
  430. }
  431. TVINSERTSTRUCT tvis;
  432. tvis.hParent = htiParent;
  433. tvis.hInsertAfter = TVI_LAST;
  434. tvis.item.mask = TVIF_TEXT | TVIF_PARAM | TVIF_IMAGE;
  435. tvis.item.pszText = szName;
  436. tvis.item.iImage = iGadget;
  437. tvis.item.lParam = (LPARAM) hgad;
  438. HTREEITEM htiNew = TreeView_InsertItem(m_hwndTree, &tvis);
  439. if (htiNew != NULL) {
  440. if (AddGadgetMessageHandler(hgad, 0, m_hgadMsg)) {
  441. Verify(SetGadgetProperty(hgad, s_pridLink, htiNew));
  442. } else {
  443. Trace("WARNING: Spy unable to attach handler on 0x%p\n", hgad);
  444. }
  445. }
  446. return htiNew;
  447. }
  448. //------------------------------------------------------------------------------
  449. HGADGET
  450. Spy::GetGadget(HTREEITEM hti)
  451. {
  452. TVITEM tvi;
  453. tvi.hItem = hti;
  454. tvi.mask = TVIF_PARAM | TVIF_HANDLE;
  455. if (TreeView_GetItem(m_hwndTree, &tvi)) {
  456. HGADGET hgadItem = (HGADGET) tvi.lParam;
  457. AssertMsg(hgadItem != NULL, "All items in the tree should have a Gadget");
  458. return hgadItem;
  459. }
  460. return NULL;
  461. }
  462. //------------------------------------------------------------------------------
  463. void
  464. Spy::DisplayContextMenu(BOOL fViaKbd)
  465. {
  466. //
  467. // Locate TreeView item
  468. //
  469. POINT ptPopup;
  470. ZeroMemory(&ptPopup, sizeof(ptPopup));
  471. HTREEITEM hti;
  472. if (fViaKbd) {
  473. //
  474. // Keyboard driven
  475. //
  476. hti = TreeView_GetSelection(m_hwndTree);
  477. if (hti != NULL) {
  478. RECT rc;
  479. TreeView_GetItemRect(m_hwndTree, hti, &rc, TRUE);
  480. ptPopup.x = rc.left;
  481. ptPopup.y = rc.bottom;
  482. ClientToScreen(m_hwndTree, &ptPopup);
  483. }
  484. } else {
  485. //
  486. // Mouse driven
  487. //
  488. TVHITTESTINFO tvht;
  489. DWORD dwPos = GetMessagePos();
  490. ptPopup.x = GET_X_LPARAM(dwPos);
  491. ptPopup.y = GET_Y_LPARAM(dwPos);
  492. tvht.pt = ptPopup;
  493. ScreenToClient(m_hwndTree, &tvht.pt);
  494. hti = TreeView_HitTest(m_hwndTree, &tvht);
  495. }
  496. //
  497. // Now have tree item and popup position
  498. //
  499. if (hti != NULL) {
  500. //
  501. // Get Gadget associated with this item
  502. //
  503. HGADGET hgad = GetGadget(hti);
  504. //
  505. // Create popup menu template
  506. //
  507. HMENU hMenu = CreatePopupMenu();
  508. if (hMenu != NULL) {
  509. BOOL fRes;
  510. const int cmdDetails = 10;
  511. fRes = AppendMenu(hMenu, MF_STRING, cmdDetails, "Details...");
  512. if (fRes) {
  513. UINT nCmd = TrackPopupMenu(hMenu, TPM_RETURNCMD | TPM_RIGHTBUTTON, ptPopup.x, ptPopup.y, 0, m_hwndTree, NULL);
  514. DestroyMenu(hMenu);
  515. //
  516. // Invoke commands
  517. //
  518. switch (nCmd)
  519. {
  520. case cmdDetails:
  521. {
  522. GMSG_QUERYDETAILS msg;
  523. msg.cbSize = sizeof(msg);
  524. msg.hgadMsg = hgad;
  525. msg.nMsg = GM_QUERY;
  526. msg.nCode = GQUERY_DETAILS;
  527. msg.nType = GQDT_HWND;
  528. msg.hOwner = m_hwndTree;
  529. DUserSendEvent(&msg, 0);
  530. }
  531. break;
  532. }
  533. }
  534. }
  535. }
  536. }
  537. //------------------------------------------------------------------------------
  538. int
  539. Spy::NumLines(int cyPxl) const
  540. {
  541. return (cyPxl - 1) / s_cyLinePxl + 1;
  542. }
  543. //------------------------------------------------------------------------------
  544. void
  545. Spy::UpdateDetails()
  546. {
  547. if (m_hgadDetails == NULL) {
  548. return;
  549. }
  550. RECT rcPxl;
  551. GetGadgetRect(m_hgadDetails, &rcPxl, SGR_CONTAINER);
  552. wsprintf(m_szRect, "(%d, %d)-(%d, %d) %d� %d",
  553. rcPxl.left, rcPxl.top, rcPxl.right, rcPxl.bottom,
  554. rcPxl.right - rcPxl.left, rcPxl.bottom - rcPxl.top);
  555. GMSG_QUERYDESC msg;
  556. msg.cbSize = sizeof(msg);
  557. msg.hgadMsg = m_hgadDetails;
  558. msg.nMsg = GM_QUERY;
  559. msg.nCode = GQUERY_DESCRIPTION;
  560. msg.szName[0] = '\0';
  561. msg.szType[0] = '\0';
  562. if (DUserSendEvent(&msg, 0) == DU_S_COMPLETE) {
  563. CopyString(m_szName, msg.szName, _countof(m_szName));
  564. CopyString(m_szType, msg.szType, _countof(m_szType));
  565. } else {
  566. m_szName[0] = '\0';
  567. m_szType[0] = '\0';
  568. }
  569. //
  570. // We are bypassinging the normal API's to directly call a
  571. // DEBUG-only function. Need to lock the Context and do FULL handle
  572. // validation.
  573. //
  574. ContextLock cl;
  575. if (cl.LockNL(ContextLock::edNone)) {
  576. DuVisual * pgadTree = ValidateVisual(m_hgadDetails);
  577. AssertMsg(pgadTree != NULL, "Should be a valid DuVisual for Spy");
  578. pgadTree->DEBUG_GetStyleDesc(m_szStyle, _countof(m_szStyle));
  579. UpdateLayoutDesc(FALSE);
  580. InvalidateRect(m_hwnd, NULL, TRUE);
  581. }
  582. }
  583. //------------------------------------------------------------------------------
  584. void
  585. Spy::UpdateLayout()
  586. {
  587. RECT rcClient;
  588. GetClientRect(m_hwnd, &rcClient);
  589. m_sizeWndPxl.cx = rcClient.right;
  590. m_sizeWndPxl.cy = rcClient.bottom;
  591. UpdateLayoutDesc(TRUE);
  592. }
  593. //------------------------------------------------------------------------------
  594. void
  595. Spy::UpdateLayoutDesc(BOOL fForceLayoutDesc)
  596. {
  597. //
  598. // Compute the number of needed lines
  599. //
  600. int cOldLines = m_cLines;
  601. m_cLines = 4;
  602. RECT rcStyle;
  603. rcStyle.left = cxBorder + cxValue;
  604. rcStyle.top = 0;
  605. rcStyle.right = m_sizeWndPxl.cx - cxBorder;
  606. rcStyle.bottom = 10000;
  607. HDC hdc = GetGdiCache()->GetTempDC();
  608. HFONT hfntOld = (HFONT) SelectObject(hdc, s_hfntDesc);
  609. int nHeight = OS()->DrawText(hdc, m_szStyle, (int) wcslen(m_szStyle), &rcStyle,
  610. DT_CALCRECT | DT_LEFT | DT_TOP | DT_WORDBREAK);
  611. SelectObject(hdc, hfntOld);
  612. GetGdiCache()->ReleaseTempDC(hdc);
  613. m_cLines += NumLines(nHeight);
  614. //
  615. // Move the Tree to provide space for the description
  616. //
  617. if ((cOldLines != m_cLines) || fForceLayoutDesc) {
  618. m_cyDescPxl = s_cyLinePxl * m_cLines + 10;
  619. m_fShowDesc = m_sizeWndPxl.cy > m_cyDescPxl;
  620. SIZE sizeTree;
  621. sizeTree.cx = m_sizeWndPxl.cx;
  622. sizeTree.cy = m_fShowDesc ? (m_sizeWndPxl.cy - m_cyDescPxl) : m_sizeWndPxl.cy;
  623. MoveWindow(m_hwndTree, 0, 0, sizeTree.cx, sizeTree.cy, TRUE);
  624. }
  625. }
  626. //------------------------------------------------------------------------------
  627. void
  628. Spy::OnPaint(HDC hdc)
  629. {
  630. HFONT hfntOld = (HFONT) SelectObject(hdc, s_hfntDesc);
  631. int nOldMode = SetBkMode(hdc, TRANSPARENT);
  632. RECT rcOutline;
  633. rcOutline.left = 2;
  634. rcOutline.top = m_sizeWndPxl.cy - m_cyDescPxl + 2;
  635. rcOutline.right = m_sizeWndPxl.cx - 1;
  636. rcOutline.bottom = m_sizeWndPxl.cy - 1;
  637. GdDrawOutlineRect(hdc, &rcOutline, s_hbrOutline);
  638. POINT pt;
  639. pt.x = cxBorder;
  640. pt.y = m_sizeWndPxl.cy - m_cyDescPxl + cyBorder;
  641. // NOTE: m_cLines should equal the number of lines displayed here
  642. PaintLine(hdc, &pt, "HGADGET: ", m_hgadDetails);
  643. PaintLine(hdc, &pt, "Name: ", m_szName, FALSE, s_hfntDescBold);
  644. PaintLine(hdc, &pt, "Type: ", m_szType);
  645. PaintLine(hdc, &pt, "Rectangle: ", m_szRect);
  646. PaintLine(hdc, &pt, "Style: ", m_szStyle, TRUE);
  647. SetBkMode(hdc, nOldMode);
  648. SelectObject(hdc, hfntOld);
  649. }
  650. class CTempSelectFont
  651. {
  652. public:
  653. CTempSelectFont(HDC hdc, HFONT hfnt)
  654. {
  655. m_hdc = hdc;
  656. m_fSelect = (hfnt != NULL);
  657. if (m_fSelect) {
  658. m_hfntOld = (HFONT) SelectObject(m_hdc, hfnt);
  659. }
  660. }
  661. ~CTempSelectFont()
  662. {
  663. if (m_fSelect) {
  664. SelectObject(m_hdc, m_hfntOld);
  665. }
  666. }
  667. BOOL m_fSelect;
  668. HDC m_hdc;
  669. HFONT m_hfntOld;
  670. };
  671. //------------------------------------------------------------------------------
  672. void
  673. Spy::PaintLine(HDC hdc, POINT * pptOffset, LPCTSTR pszName, LPCTSTR pszText, HFONT hfnt)
  674. {
  675. TextOut(hdc, pptOffset->x, pptOffset->y, pszName, (int) _tcslen(pszName));
  676. CTempSelectFont tsf(hdc, hfnt);
  677. TextOut(hdc, pptOffset->x + cxValue, pptOffset->y, pszText, (int) _tcslen(pszText));
  678. pptOffset->y += s_cyLinePxl;
  679. }
  680. //------------------------------------------------------------------------------
  681. void
  682. Spy::PaintLine(HDC hdc, POINT * pptOffset, LPCTSTR pszName, LPCWSTR pszText, BOOL fMultiline, HFONT hfnt)
  683. {
  684. TextOut(hdc, pptOffset->x, pptOffset->y, pszName, (int) _tcslen(pszName));
  685. CTempSelectFont tsf(hdc, hfnt);
  686. if (fMultiline) {
  687. RECT rcStyle;
  688. rcStyle.left = pptOffset->x + cxValue;
  689. rcStyle.top = pptOffset->y;
  690. rcStyle.right = m_sizeWndPxl.cx - cxBorder;
  691. rcStyle.bottom = 10000;
  692. int nHeight = OS()->DrawText(hdc, pszText, (int) wcslen(pszText), &rcStyle,
  693. DT_LEFT | DT_TOP | DT_WORDBREAK);
  694. pptOffset->y += NumLines(nHeight) * s_cyLinePxl;
  695. } else {
  696. OS()->TextOut(hdc, pptOffset->x + cxValue, pptOffset->y, pszText, (int) wcslen(pszText));
  697. pptOffset->y += s_cyLinePxl;
  698. }
  699. }
  700. //------------------------------------------------------------------------------
  701. void
  702. Spy::PaintLine(HDC hdc, POINT * pptOffset, LPCTSTR pszName, int nValue, HFONT hfnt)
  703. {
  704. TextOut(hdc, pptOffset->x, pptOffset->y, pszName, (int) _tcslen(pszName));
  705. CTempSelectFont tsf(hdc, hfnt);
  706. TCHAR szValue[20];
  707. _itot(nValue, szValue, 10);
  708. TextOut(hdc, pptOffset->x + cxValue, pptOffset->y, szValue, (int) _tcslen(szValue));
  709. pptOffset->y += s_cyLinePxl;
  710. }
  711. //------------------------------------------------------------------------------
  712. void
  713. Spy::PaintLine(HDC hdc, POINT * pptOffset, LPCTSTR pszName, void * pvValue, HFONT hfnt)
  714. {
  715. TextOut(hdc, pptOffset->x, pptOffset->y, pszName, (int) _tcslen(pszName));
  716. CTempSelectFont tsf(hdc, hfnt);
  717. TCHAR szValue[20];
  718. wsprintf(szValue, "0x%p", pvValue);
  719. TextOut(hdc, pptOffset->x + cxValue, pptOffset->y, szValue, (int) _tcslen(szValue));
  720. pptOffset->y += s_cyLinePxl;
  721. }
  722. #endif // DBG