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

735 lines
18 KiB

  1. /*++
  2. Copyright (c) 1999-2000 Microsoft Corporation
  3. Module Name:
  4. docwin.cpp
  5. Abstract:
  6. This module contains the code for the new doc windows.
  7. --*/
  8. #include "precomp.hxx"
  9. #pragma hdrstop
  10. #include <dbghelp.h>
  11. ULONG g_TabWidth = 32;
  12. BOOL g_DisasmActivateSource;
  13. //
  14. //
  15. //
  16. DOCWIN_DATA::DOCWIN_DATA()
  17. // State buffer isn't currently used.
  18. : EDITWIN_DATA(256)
  19. {
  20. m_enumType = DOC_WINDOW;
  21. ZeroMemory(m_szFoundFile, _tsizeof(m_szFoundFile));
  22. ZeroMemory(m_szSymFile, _tsizeof(m_szSymFile));
  23. ZeroMemory(&m_LastWriteTime, sizeof(m_LastWriteTime));
  24. m_FindSel.cpMin = 1;
  25. m_FindSel.cpMax = 0;
  26. m_FindFlags = 0;
  27. }
  28. void
  29. DOCWIN_DATA::Validate()
  30. {
  31. EDITWIN_DATA::Validate();
  32. Assert(DOC_WINDOW == m_enumType);
  33. }
  34. BOOL
  35. DOCWIN_DATA::CanGotoLine(void)
  36. {
  37. return m_TextLines > 0;
  38. }
  39. void
  40. DOCWIN_DATA::GotoLine(ULONG Line)
  41. {
  42. CHARRANGE Sel;
  43. Sel.cpMin = (LONG)SendMessage(m_hwndChild, EM_LINEINDEX, Line - 1, 0);
  44. Sel.cpMax = Sel.cpMin;
  45. SendMessage(m_hwndChild, EM_EXSETSEL, 0, (LPARAM)&Sel);
  46. }
  47. void
  48. DOCWIN_DATA::Find(PTSTR Text, ULONG Flags)
  49. {
  50. RicheditFind(m_hwndChild, Text, Flags,
  51. &m_FindSel, &m_FindFlags, TRUE);
  52. }
  53. BOOL
  54. DOCWIN_DATA::CodeExprAtCaret(PSTR Expr, PULONG64 Offset)
  55. {
  56. LRESULT LineChar;
  57. LONG Line;
  58. LineChar = SendMessage(m_hwndChild, EM_LINEINDEX, -1, 0);
  59. Line = (LONG)SendMessage(m_hwndChild, EM_EXLINEFROMCHAR, 0, LineChar);
  60. if (Line < 0)
  61. {
  62. return FALSE;
  63. }
  64. // Convert to one-based.
  65. Line++;
  66. if (Offset != NULL)
  67. {
  68. *Offset = DEBUG_INVALID_OFFSET;
  69. }
  70. if (Expr == NULL)
  71. {
  72. // Caller is just checking whether it's possible
  73. // to get an expression or not, such as the
  74. // menu enable code. This code always considers
  75. // it possible since it can't know for sure without
  76. // a full symbol check.
  77. return TRUE;
  78. }
  79. //
  80. // First attempt to resolve the source line using currently
  81. // loaded symbols. This is done directly from the UI
  82. // thread for synchronous behavior. The assumption is
  83. // that turning off symbol loads will limit the execution
  84. // time to something reasonably quick.
  85. //
  86. DEBUG_VALUE Val;
  87. HRESULT Status;
  88. sprintf(Expr, "`<U>%s:%d+`", m_pszSymFile, Line);
  89. Status = g_pUiControl->Evaluate(Expr, DEBUG_VALUE_INT64, &Val, NULL);
  90. // Don't preserve the <U>nqualified option in the actual
  91. // expression returned as it's just a temporary override.
  92. sprintf(Expr, "`%s:%d+`", m_pszSymFile, Line);
  93. if (Status == S_OK)
  94. {
  95. if (Offset != NULL)
  96. {
  97. *Offset = Val.I64;
  98. }
  99. return TRUE;
  100. }
  101. ULONG SymOpts;
  102. if (g_pUiSymbols->GetSymbolOptions(&SymOpts) == S_OK &&
  103. (SymOpts & SYMOPT_NO_UNQUALIFIED_LOADS))
  104. {
  105. // The user isn't allowing unqualified loads so
  106. // further searches won't help.
  107. return FALSE;
  108. }
  109. // We weren't able to resolve the expression with the
  110. // existing symbols so we'll need to do a full search.
  111. // This can be very expensive, so allow the user to cancel.
  112. if (!g_QuietMode)
  113. {
  114. int Mode = QuestionBox(STR_Unresolved_Source_Expr, MB_YESNOCANCEL);
  115. if (Mode == IDCANCEL)
  116. {
  117. return FALSE;
  118. }
  119. else if (Mode == IDYES)
  120. {
  121. if (g_pUiControl->Evaluate(Expr, DEBUG_VALUE_INT64,
  122. &Val, NULL) == S_OK)
  123. {
  124. if (Offset != NULL)
  125. {
  126. *Offset = Val.I64;
  127. }
  128. return TRUE;
  129. }
  130. else
  131. {
  132. return FALSE;
  133. }
  134. }
  135. }
  136. // Let the expression go without trying to further resolve it.
  137. return TRUE;
  138. }
  139. void
  140. DOCWIN_DATA::ToggleBpAtCaret(void)
  141. {
  142. LRESULT LineChar;
  143. LONG Line;
  144. LineChar = SendMessage(m_hwndChild, EM_LINEINDEX, -1, 0);
  145. Line = (LONG)SendMessage(m_hwndChild, EM_EXLINEFROMCHAR, 0, LineChar);
  146. if (Line < 0)
  147. {
  148. return;
  149. }
  150. // If we have a breakpoint on this line remove it.
  151. EDIT_HIGHLIGHT* Hl = GetLineHighlighting(Line);
  152. if (Hl != NULL && (Hl->Flags & EHL_ANY_BP))
  153. {
  154. PrintStringCommand(UIC_SILENT_EXECUTE, "bc %d", (ULONG)Hl->Data);
  155. return;
  156. }
  157. //
  158. // No breakpoint exists so add a new one.
  159. //
  160. char CodeExpr[MAX_OFFSET_EXPR];
  161. ULONG64 Offset;
  162. if (!CodeExprAtCaret(CodeExpr, &Offset))
  163. {
  164. MessageBeep(0);
  165. ErrorBox(NULL, 0, ERR_No_Code_For_File_Line);
  166. }
  167. else
  168. {
  169. if (Offset != DEBUG_INVALID_OFFSET)
  170. {
  171. char SymName[MAX_OFFSET_EXPR];
  172. ULONG64 Disp;
  173. // Check and see whether this offset maps
  174. // exactly to a symbol. If it does, use
  175. // the symbol name to be more robust in the
  176. // face of source changes.
  177. // Symbols should be loaded at this point since
  178. // we just used them to resolve the source
  179. // expression that produced Offset, so we
  180. // can safely do this on the UI thread.
  181. if (g_pUiSymbols->GetNameByOffset(Offset, SymName, sizeof(SymName),
  182. NULL, &Disp) == S_OK &&
  183. Disp == 0)
  184. {
  185. strcpy(CodeExpr, SymName);
  186. }
  187. }
  188. PrintStringCommand(UIC_SILENT_EXECUTE, "bu %s", CodeExpr);
  189. }
  190. }
  191. BOOL
  192. DOCWIN_DATA::OnCreate(void)
  193. {
  194. if (!EDITWIN_DATA::OnCreate())
  195. {
  196. return FALSE;
  197. }
  198. SendMessage(m_hwndChild, EM_SETEVENTMASK,
  199. 0, ENM_SELCHANGE | ENM_KEYEVENTS);
  200. SendMessage(m_hwndChild, EM_SETTABSTOPS, 1, (LPARAM)&g_TabWidth);
  201. return TRUE;
  202. }
  203. LRESULT
  204. DOCWIN_DATA::OnNotify(WPARAM Wpm, LPARAM Lpm)
  205. {
  206. SELCHANGE* SelChange = (SELCHANGE*)Lpm;
  207. if (SelChange->nmhdr.code == EN_SELCHANGE)
  208. {
  209. int Line = (int)
  210. SendMessage(m_hwndChild, EM_EXLINEFROMCHAR, 0,
  211. SelChange->chrg.cpMin);
  212. LRESULT LineFirst =
  213. SendMessage(m_hwndChild, EM_LINEINDEX, Line, 0);
  214. SetLineColumn_StatusBar(Line + 1,
  215. (int)(SelChange->chrg.cpMin - LineFirst) + 1);
  216. return 0;
  217. }
  218. return EDITWIN_DATA::OnNotify(Wpm, Lpm);
  219. }
  220. void
  221. DOCWIN_DATA::OnUpdate(
  222. UpdateType Type
  223. )
  224. {
  225. if (Type == UPDATE_BP ||
  226. Type == UPDATE_BUFFER ||
  227. Type == UPDATE_END_SESSION)
  228. {
  229. UpdateBpMarks();
  230. }
  231. else if (Type == UPDATE_START_SESSION)
  232. {
  233. if (m_szFoundFile[0] &&
  234. CheckForFileChanges(m_szFoundFile, &m_LastWriteTime) == IDYES)
  235. {
  236. char Found[MAX_SOURCE_PATH], Sym[MAX_SOURCE_PATH];
  237. // Save away filenames since they're copied over
  238. // on a successful load.
  239. strcpy(Found, m_szFoundFile);
  240. strcpy(Sym, m_szSymFile);
  241. if (!LoadFile(Found, Sym))
  242. {
  243. PostMessage(g_hwndMDIClient, WM_MDIDESTROY, (WPARAM)m_Win, 0);
  244. }
  245. }
  246. }
  247. }
  248. ULONG
  249. DOCWIN_DATA::GetWorkspaceSize(void)
  250. {
  251. ULONG Len = EDITWIN_DATA::GetWorkspaceSize();
  252. Len += _tcslen(m_szFoundFile) + 1;
  253. Len += _tcslen(m_szSymFile) + 1;
  254. return Len;
  255. }
  256. PUCHAR
  257. DOCWIN_DATA::SetWorkspace(PUCHAR Data)
  258. {
  259. PTSTR Str = (PTSTR)EDITWIN_DATA::SetWorkspace(Data);
  260. _tcscpy(Str, m_szFoundFile);
  261. Str += _tcslen(m_szFoundFile) + 1;
  262. _tcscpy(Str, m_szSymFile);
  263. Str += _tcslen(m_szSymFile) + 1;
  264. return (PUCHAR)Str;
  265. }
  266. PUCHAR
  267. DOCWIN_DATA::ApplyWorkspace1(PUCHAR Data, PUCHAR End)
  268. {
  269. PTSTR Found = (PTSTR)EDITWIN_DATA::ApplyWorkspace1(Data, End);
  270. PTSTR Sym = Found + _tcslen(Found) + 1;
  271. if (Found[0])
  272. {
  273. if (FindDocWindowByFileName(Found, NULL, NULL) ||
  274. !LoadFile(Found, Sym[0] ? Sym : NULL))
  275. {
  276. PostMessage(g_hwndMDIClient, WM_MDIDESTROY, (WPARAM)m_Win, 0);
  277. }
  278. }
  279. return (PUCHAR)(Sym + _tcslen(Sym) + 1);
  280. }
  281. void
  282. DOCWIN_DATA::UpdateBpMarks(void)
  283. {
  284. if (m_TextLines == 0 ||
  285. g_BpBuffer->UiLockForRead() != S_OK)
  286. {
  287. return;
  288. }
  289. SendMessage(m_hwndChild, WM_SETREDRAW, FALSE, 0);
  290. // Remove existing BP highlights.
  291. RemoveAllHighlights(EHL_ANY_BP);
  292. //
  293. // Highlight every line that matches a breakpoint.
  294. //
  295. BpBufferData* BpData = (BpBufferData*)g_BpBuffer->GetDataBuffer();
  296. ULONG i;
  297. for (i = 0; i < g_BpCount; i++)
  298. {
  299. if (BpData[i].FileOffset)
  300. {
  301. PSTR FileSpace;
  302. ULONG Line;
  303. PSTR FileStop, MatchStop;
  304. ULONG HlFlags;
  305. FileSpace = (PSTR)g_BpBuffer->GetDataBuffer() +
  306. BpData[i].FileOffset;
  307. // Adjust to zero-based.
  308. Line = *(ULONG UNALIGNED *)FileSpace - 1;
  309. FileSpace += sizeof(Line);
  310. // If this document's file matches some suffix
  311. // of the breakpoint's file at the path component
  312. // level then do the highlight. This can result in
  313. // extra highlights for multiple files with the same
  314. // name but in different directories. That's a rare
  315. // enough problem to wait for somebody to complain
  316. // before trying to hack some better check up.
  317. if (SymMatchFileName(FileSpace, (PSTR)m_pszSymFile,
  318. &FileStop, &MatchStop) ||
  319. *MatchStop == '\\' ||
  320. *MatchStop == '/' ||
  321. *MatchStop == ':')
  322. {
  323. if (BpData[i].Flags & DEBUG_BREAKPOINT_ENABLED)
  324. {
  325. HlFlags = EHL_ENABLED_BP;
  326. }
  327. else
  328. {
  329. HlFlags = EHL_DISABLED_BP;
  330. }
  331. EDIT_HIGHLIGHT* Hl = AddHighlight(Line, HlFlags);
  332. if (Hl != NULL)
  333. {
  334. Hl->Data = BpData[i].Id;
  335. }
  336. }
  337. }
  338. }
  339. UnlockStateBuffer(g_BpBuffer);
  340. SendMessage(m_hwndChild, WM_SETREDRAW, TRUE, 0);
  341. InvalidateRect(m_hwndChild, NULL, TRUE);
  342. }
  343. DWORD
  344. CALLBACK
  345. EditStreamCallback(
  346. DWORD_PTR dwFileHandle, // application-defined value
  347. LPBYTE pbBuff, // data buffer
  348. LONG cb, // number of bytes to read or write
  349. LONG *pcb // number of bytes transferred
  350. )
  351. {
  352. HRESULT Status;
  353. PathFile* File = (PathFile*)dwFileHandle;
  354. if ((Status = File->Read(pbBuff, cb, (PDWORD)pcb)) != S_OK)
  355. {
  356. return Status;
  357. }
  358. // Edit out page-break characters (^L's) as richedit
  359. // gives them their own line which throws off line numbers.
  360. while (cb-- > 0)
  361. {
  362. if (*pbBuff == '\f')
  363. {
  364. *pbBuff = ' ';
  365. }
  366. pbBuff++;
  367. }
  368. return 0; // No error
  369. }
  370. BOOL
  371. DOCWIN_DATA::LoadFile(
  372. PCTSTR pszFoundFile,
  373. PCTSTR pszSymFile
  374. )
  375. /*++
  376. Returns
  377. TRUE - Success, file opened and loaded
  378. FALSE - Failure, file not loaded
  379. --*/
  380. {
  381. Assert(pszFoundFile);
  382. BOOL bRet = TRUE;
  383. HCURSOR hcursor = NULL;
  384. EDITSTREAM editstr = {0};
  385. PathFile *File = NULL;
  386. if ((OpenPathFile(pszFoundFile, 0, &File)) != S_OK)
  387. {
  388. ErrorBox(NULL, 0, ERR_File_Open, pszFoundFile);
  389. bRet = FALSE;
  390. goto exit;
  391. }
  392. // Store last write time to check for file changes.
  393. if (File->GetLastWriteTime(&m_LastWriteTime) != S_OK)
  394. {
  395. ZeroMemory(&m_LastWriteTime, sizeof(m_LastWriteTime));
  396. }
  397. // Set the Hour glass cursor
  398. hcursor = SetCursor( LoadCursor(NULL, IDC_WAIT) );
  399. // Select all of the text so that it will be replaced
  400. SendMessage(m_hwndChild, EM_SETSEL, 0, -1);
  401. // Put the text into the window
  402. editstr.dwCookie = (DWORD_PTR)File;
  403. editstr.pfnCallback = EditStreamCallback;
  404. SendMessage(m_hwndChild,
  405. EM_STREAMIN,
  406. SF_TEXT,
  407. (LPARAM) &editstr
  408. );
  409. // Restore cursor
  410. SetCursor(hcursor);
  411. _tcsncpy(m_szFoundFile, pszFoundFile, _tsizeof(m_szFoundFile) - 1 );
  412. m_szFoundFile[ _tsizeof(m_szFoundFile) - 1 ] = 0;
  413. if (pszSymFile != NULL && pszSymFile[0])
  414. {
  415. _tcsncpy(m_szSymFile, pszSymFile, _tsizeof(m_szSymFile) - 1 );
  416. m_szSymFile[ _tsizeof(m_szSymFile) - 1 ] = 0;
  417. m_pszSymFile = m_szSymFile;
  418. }
  419. else
  420. {
  421. // No symbol file information so just use the found filename.
  422. m_szSymFile[0] = 0;
  423. m_pszSymFile = strrchr(m_szFoundFile, '\\');
  424. if (m_pszSymFile == NULL)
  425. {
  426. m_pszSymFile = strrchr(m_szFoundFile, '/');
  427. if (m_pszSymFile == NULL)
  428. {
  429. m_pszSymFile = strrchr(m_szFoundFile, ':');
  430. if (m_pszSymFile == NULL)
  431. {
  432. m_pszSymFile = m_szFoundFile - 1;
  433. }
  434. }
  435. }
  436. m_pszSymFile++;
  437. }
  438. SetWindowText(m_Win, m_szFoundFile);
  439. if (SendMessage(m_hwndChild, WM_GETTEXTLENGTH, 0, 0) == 0)
  440. {
  441. m_TextLines = 0;
  442. }
  443. else
  444. {
  445. m_TextLines = (ULONG)SendMessage(m_hwndChild, EM_GETLINECOUNT, 0, 0);
  446. }
  447. if (g_LineMarkers)
  448. {
  449. // Insert marker space before every line.
  450. for (ULONG i = 0; i < m_TextLines; i++)
  451. {
  452. CHARRANGE Ins;
  453. Ins.cpMin = (LONG)SendMessage(m_hwndChild, EM_LINEINDEX, i, 0);
  454. Ins.cpMax = Ins.cpMin;
  455. SendMessage(m_hwndChild, EM_EXSETSEL, 0, (LPARAM)&Ins);
  456. SendMessage(m_hwndChild, EM_REPLACESEL, FALSE, (LPARAM)" ");
  457. }
  458. }
  459. // Request that the engine update the line map for the file.
  460. UiRequestRead();
  461. exit:
  462. delete File;
  463. return bRet;
  464. }
  465. BOOL
  466. SameFileName(PCSTR Name1, PCSTR Name2)
  467. {
  468. while (*Name1)
  469. {
  470. if (!(((*Name1 == '\\' || *Name1 == '/') &&
  471. (*Name2 == '\\' || *Name2 == '/')) ||
  472. toupper(*Name1) == toupper(*Name2)))
  473. {
  474. return FALSE;
  475. }
  476. Name1++;
  477. Name2++;
  478. }
  479. return *Name2 == 0;
  480. }
  481. BOOL
  482. FindDocWindowByFileName(
  483. IN PCTSTR pszFile,
  484. OPTIONAL HWND *phwnd,
  485. OPTIONAL PDOCWIN_DATA *ppDocWinData
  486. )
  487. /*++
  488. Returns
  489. TRUE - If the window is currently open.
  490. FALSE - Not currently open.
  491. --*/
  492. {
  493. Assert(pszFile);
  494. PLIST_ENTRY Entry;
  495. PDOCWIN_DATA pTmp;
  496. Entry = g_ActiveWin.Flink;
  497. while (Entry != &g_ActiveWin)
  498. {
  499. pTmp = (PDOCWIN_DATA)ACTIVE_WIN_ENTRY(Entry);
  500. if ( pTmp->m_enumType == DOC_WINDOW &&
  501. SameFileName(pTmp->m_szFoundFile, pszFile) )
  502. {
  503. if (ppDocWinData)
  504. {
  505. *ppDocWinData = pTmp;
  506. }
  507. if (phwnd)
  508. {
  509. *phwnd = pTmp->m_Win;
  510. }
  511. return TRUE;
  512. }
  513. Entry = Entry->Flink;
  514. }
  515. return FALSE;
  516. }
  517. BOOL
  518. OpenOrActivateFile(PCSTR FoundFile, PCSTR SymFile, ULONG Line,
  519. BOOL Activate, BOOL UserActivated)
  520. {
  521. HWND hwndDoc = NULL;
  522. PDOCWIN_DATA pDoc;
  523. BOOL Activated = FALSE;
  524. if ( FindDocWindowByFileName( FoundFile, &hwndDoc, &pDoc) )
  525. {
  526. if (Activate)
  527. {
  528. // Found it. Now activate it.
  529. ActivateMDIChild(hwndDoc, UserActivated);
  530. Activated = TRUE;
  531. }
  532. }
  533. else
  534. {
  535. hwndDoc = NewDoc_CreateWindow(g_hwndMDIClient);
  536. if (hwndDoc == NULL)
  537. {
  538. return FALSE;
  539. }
  540. pDoc = GetDocWinData(hwndDoc);
  541. Assert(pDoc);
  542. if (!pDoc->LoadFile(FoundFile, SymFile))
  543. {
  544. DestroyWindow(pDoc->m_Win);
  545. return FALSE;
  546. }
  547. Activated = TRUE;
  548. }
  549. // Success. Now highlight the line.
  550. pDoc->SetCurrentLineHighlight(Line);
  551. return Activated;
  552. }
  553. void
  554. UpdateCodeDisplay(
  555. ULONG64 Ip,
  556. PCSTR FoundFile,
  557. PCSTR SymFile,
  558. ULONG Line,
  559. BOOL UserActivated
  560. )
  561. {
  562. // Update the disassembly window if there's one
  563. // active or there's no source information.
  564. BOOL Activated = FALSE;
  565. HWND hwndDisasm = GetDisasmHwnd();
  566. if (hwndDisasm == NULL && FoundFile == NULL &&
  567. (g_WinOptions & WOPT_AUTO_DISASM))
  568. {
  569. // No disassembly window around and no source so create one.
  570. hwndDisasm = NewDisasm_CreateWindow(g_hwndMDIClient);
  571. }
  572. if (hwndDisasm != NULL)
  573. {
  574. PDISASMWIN_DATA pDis = GetDisasmWinData(hwndDisasm);
  575. Assert(pDis);
  576. pDis->SetCurInstr(Ip);
  577. }
  578. if (FoundFile != NULL)
  579. {
  580. //
  581. // We now know the file name and line number. Either
  582. // it's open or we open it.
  583. //
  584. Activated = OpenOrActivateFile(FoundFile, SymFile, Line,
  585. GetSrcMode_StatusBar() ||
  586. g_DisasmActivateSource,
  587. UserActivated);
  588. }
  589. else
  590. {
  591. // No source file was found so make sure no
  592. // doc windows have a highlight.
  593. EDITWIN_DATA::RemoveActiveWinHighlights(1 << DOC_WINDOW,
  594. EHL_CURRENT_LINE);
  595. }
  596. if ((!Activated || !GetSrcMode_StatusBar()) && hwndDisasm != NULL)
  597. {
  598. // No window has been activated yet so fall back
  599. // on activating the disassembly window.
  600. ActivateMDIChild(hwndDisasm, UserActivated);
  601. }
  602. }
  603. void
  604. SetTabWidth(ULONG TabWidth)
  605. {
  606. PLIST_ENTRY Entry;
  607. PDOCWIN_DATA DocData;
  608. g_TabWidth = TabWidth;
  609. if (g_Workspace != NULL)
  610. {
  611. g_Workspace->SetUlong(WSP_GLOBAL_TAB_WIDTH, TabWidth);
  612. }
  613. Entry = g_ActiveWin.Flink;
  614. while (Entry != &g_ActiveWin)
  615. {
  616. DocData = (PDOCWIN_DATA)ACTIVE_WIN_ENTRY(Entry);
  617. if (DocData->m_enumType == DOC_WINDOW)
  618. {
  619. SendMessage(DocData->m_hwndChild, EM_SETTABSTOPS,
  620. 1, (LPARAM)&g_TabWidth);
  621. }
  622. Entry = Entry->Flink;
  623. }
  624. }