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.

1439 lines
34 KiB

  1. /*++
  2. Copyright (c) 1999-2001 Microsoft Corporation
  3. Module Name:
  4. wrkspace.cpp
  5. Abstract:
  6. This module contains the workspace implementation.
  7. --*/
  8. #include "precomp.hxx"
  9. #pragma hdrstop
  10. // #define DBG_WSP
  11. #define WSP_ALIGN(Size) (((Size) + 7) & ~7)
  12. #define WSP_GROW_BY 1024
  13. #if DBG
  14. #define SCORCH_ENTRY(Entry) \
  15. memset((Entry) + 1, 0xdb, (Entry)->FullSize - sizeof(*(Entry)))
  16. #else
  17. #define SCORCH_ENTRY(Entry)
  18. #endif
  19. ULONG g_WspSwitchKey;
  20. TCHAR g_WspSwitchValue[MAX_PATH];
  21. BOOL g_WspSwitchBufferAvailable = TRUE;
  22. Workspace* g_Workspace;
  23. BOOL g_ExplicitWorkspace;
  24. char* g_WorkspaceKeyNames[] =
  25. {
  26. "",
  27. "Kernel",
  28. "User",
  29. "Dump",
  30. "Remote",
  31. "Explicit",
  32. };
  33. char* g_WorkspaceDefaultName = "Default";
  34. char* g_WorkspaceKeyDescriptions[] =
  35. {
  36. "Base workspace",
  37. "Kernel mode workspaces",
  38. "User mode workspaces",
  39. "Dump file workspaces",
  40. "Remote client workspaces",
  41. "User-saved workspaces",
  42. };
  43. Workspace::Workspace(void)
  44. {
  45. m_Flags = 0;
  46. m_Data = NULL;
  47. m_DataLen = 0;
  48. m_DataUsed = 0;
  49. m_Key = WSP_NAME_BASE;
  50. m_Value = NULL;
  51. }
  52. Workspace::~Workspace(void)
  53. {
  54. free(m_Data);
  55. free(m_Value);
  56. }
  57. WSP_ENTRY*
  58. Workspace::Get(WSP_TAG Tag)
  59. {
  60. WSP_ENTRY* Entry = NULL;
  61. while ((Entry = NextEntry(Entry)) != NULL)
  62. {
  63. if (Entry->Tag == Tag)
  64. {
  65. return Entry;
  66. }
  67. }
  68. return NULL;
  69. }
  70. WSP_ENTRY*
  71. Workspace::GetNext(WSP_ENTRY* Entry, WSP_TAG Tag, WSP_TAG TagMask)
  72. {
  73. while ((Entry = NextEntry(Entry)) != NULL)
  74. {
  75. if ((Entry->Tag & TagMask) == Tag)
  76. {
  77. return Entry;
  78. }
  79. }
  80. return NULL;
  81. }
  82. WSP_ENTRY*
  83. Workspace::GetString(WSP_TAG Tag, PSTR Str, ULONG MaxSize)
  84. {
  85. WSP_ENTRY* Entry = Get(Tag);
  86. if (Entry != NULL)
  87. {
  88. if (Entry->DataSize > MaxSize)
  89. {
  90. return NULL;
  91. }
  92. strcpy(Str, WSP_ENTRY_DATA(PSTR, Entry));
  93. }
  94. return Entry;
  95. }
  96. WSP_ENTRY*
  97. Workspace::GetAllocString(WSP_TAG Tag, PSTR* Str)
  98. {
  99. WSP_ENTRY* Entry = Get(Tag);
  100. if (Entry != NULL)
  101. {
  102. *Str = (PSTR)malloc(Entry->DataSize);
  103. if (*Str == NULL)
  104. {
  105. return NULL;
  106. }
  107. strcpy(*Str, WSP_ENTRY_DATA(PSTR, Entry));
  108. }
  109. return Entry;
  110. }
  111. WSP_ENTRY*
  112. Workspace::GetBuffer(WSP_TAG Tag, PVOID Buf, ULONG Size)
  113. {
  114. WSP_ENTRY* Entry = Get(Tag);
  115. if (Entry != NULL)
  116. {
  117. if (Entry->DataSize != Size)
  118. {
  119. return NULL;
  120. }
  121. memcpy(Buf, WSP_ENTRY_DATA(PUCHAR, Entry), Size);
  122. }
  123. return Entry;
  124. }
  125. WSP_ENTRY*
  126. Workspace::Set(WSP_TAG Tag, ULONG Size)
  127. {
  128. WSP_ENTRY* Entry;
  129. ULONG FullSize;
  130. // Compute full rounded size.
  131. FullSize = sizeof(WSP_ENTRY) + WSP_ALIGN(Size);
  132. // Check and see if there's already an entry.
  133. Entry = Get(Tag);
  134. if (Entry != NULL)
  135. {
  136. // If it's already large enough use it and
  137. // pack in remaining data.
  138. if (Entry->FullSize >= FullSize)
  139. {
  140. ULONG Pack = Entry->FullSize - FullSize;
  141. if (Pack > 0)
  142. {
  143. PackData((PUCHAR)Entry + FullSize, Pack);
  144. Entry->FullSize = (USHORT)FullSize;
  145. }
  146. Entry->DataSize = (USHORT)Size;
  147. SCORCH_ENTRY(Entry);
  148. m_Flags |= WSPF_DIRTY_WRITE;
  149. return Entry;
  150. }
  151. // Entry is too small so remove it.
  152. PackData((PUCHAR)Entry, Entry->FullSize);
  153. }
  154. return Add(Tag, Size);
  155. }
  156. WSP_ENTRY*
  157. Workspace::SetString(WSP_TAG Tag, PSTR Str)
  158. {
  159. ULONG Size = strlen(Str) + 1;
  160. WSP_ENTRY* Entry = Set(Tag, Size);
  161. if (Entry != NULL)
  162. {
  163. memcpy(WSP_ENTRY_DATA(PSTR, Entry), Str, Size);
  164. }
  165. return Entry;
  166. }
  167. WSP_ENTRY*
  168. Workspace::SetStrings(WSP_TAG Tag, ULONG Count, PSTR* Strs)
  169. {
  170. ULONG i;
  171. ULONG Size = 0;
  172. for (i = 0; i < Count; i++)
  173. {
  174. Size += strlen(Strs[i]) + 1;
  175. }
  176. // Put a double terminator at the very end.
  177. Size++;
  178. WSP_ENTRY* Entry = Set(Tag, Size);
  179. if (Entry != NULL)
  180. {
  181. PSTR Data = WSP_ENTRY_DATA(PSTR, Entry);
  182. for (i = 0; i < Count; i++)
  183. {
  184. Size = strlen(Strs[i]) + 1;
  185. memcpy(Data, Strs[i], Size);
  186. Data += Size;
  187. }
  188. *Data = 0;
  189. }
  190. return Entry;
  191. }
  192. WSP_ENTRY*
  193. Workspace::SetBuffer(WSP_TAG Tag, PVOID Buf, ULONG Size)
  194. {
  195. WSP_ENTRY* Entry = Set(Tag, Size);
  196. if (Entry != NULL)
  197. {
  198. memcpy(WSP_ENTRY_DATA(PUCHAR, Entry), Buf, Size);
  199. }
  200. return Entry;
  201. }
  202. WSP_ENTRY*
  203. Workspace::Add(WSP_TAG Tag, ULONG Size)
  204. {
  205. // Compute full rounded size.
  206. ULONG FullSize = sizeof(WSP_ENTRY) + WSP_ALIGN(Size);
  207. WSP_ENTRY* Entry = AllocateEntry(FullSize);
  208. if (Entry != NULL)
  209. {
  210. Entry->Tag = Tag;
  211. Entry->FullSize = (USHORT)FullSize;
  212. Entry->DataSize = (USHORT)Size;
  213. SCORCH_ENTRY(Entry);
  214. m_Flags |= WSPF_DIRTY_WRITE;
  215. }
  216. return Entry;
  217. }
  218. ULONG
  219. Workspace::Delete(WSP_TAG Tag, WSP_TAG TagMask)
  220. {
  221. ULONG Deleted = 0;
  222. WSP_ENTRY* Entry = NextEntry(NULL);
  223. while (Entry != NULL)
  224. {
  225. if ((Entry->Tag & TagMask) == Tag)
  226. {
  227. PackData((PUCHAR)Entry, Entry->FullSize);
  228. Deleted++;
  229. m_Flags |= WSPF_DIRTY_WRITE;
  230. // Check and see if we packed away the last entry.
  231. if (!ValidEntry(Entry))
  232. {
  233. break;
  234. }
  235. }
  236. else
  237. {
  238. Entry = NextEntry(Entry);
  239. }
  240. }
  241. return Deleted;
  242. }
  243. void
  244. Workspace::Empty(void)
  245. {
  246. // Reset used to just the header.
  247. m_DataUsed = sizeof(WSP_HEADER);
  248. // Nothing is dirty now except the write of emptiness.
  249. m_Flags = (m_Flags & ~WSPF_DIRTY_ALL) | WSPF_DIRTY_WRITE;
  250. }
  251. HRESULT
  252. Workspace::Create(ULONG Key, PTSTR Value,
  253. Workspace** NewWsp)
  254. {
  255. Workspace* Wsp = new Workspace;
  256. if (Wsp == NULL)
  257. {
  258. return E_OUTOFMEMORY;
  259. }
  260. Wsp->m_Key = Key;
  261. if (Value != NULL)
  262. {
  263. Wsp->m_Value = _tcsdup(Value);
  264. if (Wsp->m_Value == NULL)
  265. {
  266. delete Wsp;
  267. return E_OUTOFMEMORY;
  268. }
  269. }
  270. WSP_ENTRY* Entry;
  271. WSP_HEADER* Header;
  272. // Allocate intial space for the header and eight
  273. // small entries. The workspace grows by large amounts
  274. // so this will immediately allocate a reasonable chunk.
  275. Entry = Wsp->AllocateEntry(sizeof(WSP_HEADER) +
  276. 8 * (sizeof(WSP_ENTRY) + 2 * sizeof(ULONG64)));
  277. if (Entry == NULL)
  278. {
  279. delete Wsp;
  280. return E_OUTOFMEMORY;
  281. }
  282. Header = (WSP_HEADER*)Entry;
  283. Header->Signature = WSP_SIGNATURE;
  284. Header->Version = WSP_VERSION;
  285. // Reset used to just the header.
  286. Wsp->m_DataUsed = sizeof(*Header);
  287. // Start out dirty so the workspace will be written
  288. // out and therefore can be opened later.
  289. Wsp->m_Flags |= WSPF_DIRTY_WRITE;
  290. *NewWsp = Wsp;
  291. return S_OK;
  292. }
  293. HRESULT
  294. Workspace::Read(ULONG Key, PTSTR Value,
  295. Workspace** NewWsp)
  296. {
  297. // Make sure basic structures preserve alignment.
  298. C_ASSERT(sizeof(WSP_HEADER) == WSP_ALIGN(sizeof(WSP_HEADER)));
  299. C_ASSERT(sizeof(WSP_ENTRY) == WSP_ALIGN(sizeof(WSP_ENTRY)));
  300. C_ASSERT(sizeof(WSP_COMMONWIN_HEADER) ==
  301. WSP_ALIGN(sizeof(WSP_COMMONWIN_HEADER)));
  302. HRESULT Status;
  303. Workspace* Wsp = new Workspace;
  304. if (Wsp == NULL)
  305. {
  306. Status = E_OUTOFMEMORY;
  307. goto EH_Fail;
  308. }
  309. Wsp->m_Key = Key;
  310. if (Value != NULL)
  311. {
  312. Wsp->m_Value = _tcsdup(Value);
  313. if (Wsp->m_Value == NULL)
  314. {
  315. delete Wsp;
  316. return E_OUTOFMEMORY;
  317. }
  318. }
  319. HKEY RegKey;
  320. LONG RegStatus;
  321. BOOL InPrimary;
  322. //
  323. // First check and see if the value exists under the
  324. // primary key. If not, check the secondary key.
  325. //
  326. RegKey = OpenKey(TRUE, Key, FALSE);
  327. if (RegKey)
  328. {
  329. RegStatus = RegQueryValueEx(RegKey, Value, NULL, NULL, NULL, NULL);
  330. if (RegStatus != ERROR_SUCCESS && RegStatus != ERROR_MORE_DATA)
  331. {
  332. RegCloseKey(RegKey);
  333. RegKey = NULL;
  334. }
  335. }
  336. if (RegKey == NULL)
  337. {
  338. RegKey = OpenKey(FALSE, Key, FALSE);
  339. if (RegKey == NULL)
  340. {
  341. Status = E_NOINTERFACE;
  342. goto EH_Wsp;
  343. }
  344. InPrimary = FALSE;
  345. }
  346. else
  347. {
  348. InPrimary = TRUE;
  349. }
  350. DWORD Type;
  351. DWORD Size;
  352. Size = 0;
  353. RegStatus = RegQueryValueEx(RegKey, Value, NULL, &Type, NULL, &Size);
  354. if (RegStatus != ERROR_SUCCESS && RegStatus != ERROR_MORE_DATA)
  355. {
  356. if (RegStatus == ERROR_FILE_NOT_FOUND)
  357. {
  358. Status = E_NOINTERFACE;
  359. }
  360. else
  361. {
  362. Status = HRESULT_FROM_WIN32(RegStatus);
  363. }
  364. goto EH_Key;
  365. }
  366. if (Type != REG_BINARY ||
  367. WSP_ALIGN(Size) != Size)
  368. {
  369. Status = E_INVALIDARG;
  370. goto EH_Key;
  371. }
  372. WSP_ENTRY* Entry;
  373. WSP_HEADER* Header;
  374. Entry = Wsp->AllocateEntry(Size);
  375. if (Entry == NULL)
  376. {
  377. Status = E_OUTOFMEMORY;
  378. goto EH_Key;
  379. }
  380. Header = (WSP_HEADER*)Entry;
  381. if (RegQueryValueEx(RegKey, Value, NULL, &Type, (LPBYTE)Header, &Size) !=
  382. ERROR_SUCCESS ||
  383. Header->Signature != WSP_SIGNATURE ||
  384. Header->Version != WSP_VERSION)
  385. {
  386. Status = E_INVALIDARG;
  387. goto EH_Key;
  388. }
  389. RegCloseKey(RegKey);
  390. //
  391. // If the workspace was read from the secondary key
  392. // migrate it to the primary and remove the secondary
  393. // entry.
  394. //
  395. if (!InPrimary)
  396. {
  397. if (Wsp->WriteReg() == S_OK)
  398. {
  399. Wsp->DeleteReg(FALSE);
  400. }
  401. }
  402. *NewWsp = Wsp;
  403. return S_OK;
  404. EH_Key:
  405. RegCloseKey(RegKey);
  406. EH_Wsp:
  407. delete Wsp;
  408. EH_Fail:
  409. return Status;
  410. }
  411. HRESULT
  412. Workspace::ChangeName(ULONG Key, PTSTR Value, BOOL Force)
  413. {
  414. if (!Force)
  415. {
  416. HKEY RegKey;
  417. //
  418. // Check and see if a workspace entry already
  419. // exists under the given name. We only need
  420. // to check the primary key as we're only concerned
  421. // with overwriting and writing always occurs
  422. // to the primary key.
  423. //
  424. RegKey = OpenKey(TRUE, Key, FALSE);
  425. if (RegKey != NULL)
  426. {
  427. LONG RegStatus;
  428. RegStatus = RegQueryValueEx(RegKey, Value, NULL, NULL,
  429. NULL, NULL);
  430. RegCloseKey(RegKey);
  431. if (RegStatus == ERROR_SUCCESS || RegStatus == ERROR_MORE_DATA)
  432. {
  433. return HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS);
  434. }
  435. }
  436. }
  437. //
  438. // Swap the workspace name.
  439. //
  440. PTSTR NewValue;
  441. if (Value != NULL)
  442. {
  443. NewValue = _tcsdup(Value);
  444. if (NewValue == NULL)
  445. {
  446. return E_OUTOFMEMORY;
  447. }
  448. }
  449. else
  450. {
  451. NewValue = NULL;
  452. }
  453. delete m_Value;
  454. m_Key = Key;
  455. m_Value = NewValue;
  456. // Need to write data out to the new location.
  457. m_Flags |= WSPF_DIRTY_WRITE;
  458. return S_OK;
  459. }
  460. void
  461. Workspace::UpdateBreakpointInformation(void)
  462. {
  463. HRESULT Status;
  464. Status = g_BpCmdsBuffer->UiLockForRead();
  465. if (Status == S_OK)
  466. {
  467. // Clear old information.
  468. Delete(WSP_GLOBAL_BREAKPOINTS, WSP_TAG_MASK);
  469. // Only save an entry if there are breakpoints.
  470. // Minimum output is a newline and terminator so
  471. // don't count those.
  472. if (g_BpCmdsBuffer->GetDataLen() > 2)
  473. {
  474. PSTR Cmds = (PSTR)g_BpCmdsBuffer->GetDataBuffer();
  475. SetString(WSP_GLOBAL_BREAKPOINTS, Cmds);
  476. }
  477. UnlockStateBuffer(g_BpCmdsBuffer);
  478. }
  479. }
  480. void
  481. Workspace::UpdateWindowInformation(void)
  482. {
  483. // Clear old information.
  484. Delete(DEF_WSP_TAG(WSP_GROUP_WINDOW, 0), WSP_GROUP_MASK);
  485. //
  486. // Record the frame window state.
  487. //
  488. WINDOWPLACEMENT Place;
  489. Place.length = sizeof(Place);
  490. GetWindowPlacement(g_hwndFrame, &Place);
  491. SetBuffer(WSP_WINDOW_FRAME_PLACEMENT, &Place, sizeof(Place));
  492. //
  493. // Persist windows from the bottom of the Z order up
  494. // so that when they're recreated in the same order
  495. // the Z order is also recreated.
  496. //
  497. HWND Win = MDIGetActive(g_hwndMDIClient, NULL);
  498. if (Win == NULL ||
  499. (Win = GetWindow(Win, GW_HWNDLAST)) == NULL)
  500. {
  501. // No windows.
  502. return;
  503. }
  504. while (Win != NULL)
  505. {
  506. PCOMMONWIN_DATA WinData = GetCommonWinData(Win);
  507. if (WinData != NULL)
  508. {
  509. WSP_ENTRY* Entry;
  510. ULONG Size;
  511. Size = WinData->GetWorkspaceSize();
  512. Entry = Add(WSP_WINDOW_COMMONWIN_1,
  513. Size + sizeof(WSP_COMMONWIN_HEADER));
  514. if (Entry != NULL)
  515. {
  516. WSP_COMMONWIN_HEADER* Hdr =
  517. WSP_ENTRY_DATA(WSP_COMMONWIN_HEADER*, Entry);
  518. Hdr->Type = WinData->m_enumType;
  519. Hdr->Reserved = 0;
  520. if (Size > 0)
  521. {
  522. WinData->SetWorkspace((PUCHAR)(Hdr + 1));
  523. }
  524. }
  525. }
  526. Win = GetWindow(Win, GW_HWNDPREV);
  527. }
  528. }
  529. void
  530. Workspace::UpdateLogFileInformation(void)
  531. {
  532. HRESULT Status;
  533. char LogFile[MAX_PATH];
  534. BOOL Append;
  535. ULONG FileLen;
  536. Status = g_pUiControl->GetLogFile(LogFile, sizeof(LogFile), NULL,
  537. &Append);
  538. if (Status != S_OK && Status != E_NOINTERFACE)
  539. {
  540. return;
  541. }
  542. // Clear old information.
  543. Delete(WSP_GLOBAL_LOG_FILE, WSP_TAG_MASK);
  544. if (Status == E_NOINTERFACE)
  545. {
  546. // No log is open.
  547. return;
  548. }
  549. FileLen = strlen(LogFile) + 1;
  550. WSP_ENTRY* Entry = Set(WSP_GLOBAL_LOG_FILE, sizeof(BOOL) + FileLen);
  551. if (Entry != NULL)
  552. {
  553. PSTR Data = WSP_ENTRY_DATA(PSTR, Entry);
  554. *(PBOOL)Data = Append;
  555. strcpy(Data + sizeof(Append), LogFile);
  556. }
  557. }
  558. void
  559. Workspace::UpdatePathInformation(void)
  560. {
  561. HRESULT Status;
  562. char Path[MAX_ENGINE_PATH];
  563. Status = g_pUiSymbols->GetSymbolPath(Path, sizeof(Path), NULL);
  564. if (Status == S_OK)
  565. {
  566. SetString(WSP_GLOBAL_SYMBOL_PATH, Path);
  567. }
  568. Status = g_pUiSymbols->GetImagePath(Path, sizeof(Path), NULL);
  569. if (Status == S_OK)
  570. {
  571. SetString(WSP_GLOBAL_IMAGE_PATH, Path);
  572. }
  573. Status = g_pUiSymbols->GetSourcePath(Path, sizeof(Path), NULL);
  574. if (Status == S_OK)
  575. {
  576. SetString(WSP_GLOBAL_SOURCE_PATH, Path);
  577. }
  578. // Local source path is only set explicitly.
  579. }
  580. void
  581. Workspace::UpdateFilterInformation(void)
  582. {
  583. HRESULT Status;
  584. Status = g_FilterBuffer->UiLockForRead();
  585. if (Status == S_OK)
  586. {
  587. // Clear old information.
  588. Delete(WSP_GLOBAL_FILTERS, WSP_TAG_MASK);
  589. // Only save an entry if there are changes.
  590. // Minimum output is a newline and terminator so
  591. // don't count those.
  592. if (g_FilterWspCmdsOffset < g_FilterBuffer->GetDataLen() - 2)
  593. {
  594. PSTR Cmds = (PSTR)g_FilterBuffer->GetDataBuffer() +
  595. g_FilterWspCmdsOffset;
  596. SetString(WSP_GLOBAL_FILTERS, Cmds);
  597. }
  598. UnlockStateBuffer(g_FilterBuffer);
  599. }
  600. }
  601. void
  602. Workspace::UpdateMruListInformation(void)
  603. {
  604. ULONG Size;
  605. WSP_ENTRY* Entry;
  606. // Clear old information.
  607. Delete(WSP_GLOBAL_MRU_LIST, WSP_TAG_MASK);
  608. Size = GetMruSize();
  609. Entry = Set(WSP_GLOBAL_MRU_LIST, Size);
  610. if (Entry != NULL)
  611. {
  612. WriteMru(WSP_ENTRY_DATA(PUCHAR, Entry));
  613. }
  614. }
  615. HRESULT
  616. Workspace::WriteReg(void)
  617. {
  618. // Writing always occurs to the primary key.
  619. HKEY RegKey = OpenKey(TRUE, m_Key, TRUE);
  620. if (RegKey == NULL)
  621. {
  622. return E_FAIL;
  623. }
  624. LONG Status = RegSetValueEx(RegKey, m_Value, 0, REG_BINARY,
  625. m_Data, m_DataUsed);
  626. RegCloseKey(RegKey);
  627. if (Status != ERROR_SUCCESS)
  628. {
  629. return HRESULT_FROM_WIN32(Status);
  630. }
  631. else
  632. {
  633. m_Flags &= ~WSPF_DIRTY_ALL;
  634. return S_OK;
  635. }
  636. }
  637. void
  638. Workspace::DeleteReg(BOOL Primary)
  639. {
  640. DeleteRegKey(Primary, m_Key, m_Value);
  641. // We don't want to leave any dirty bits
  642. // on because the workspace would just be written
  643. // out again at the next flush.
  644. m_Flags &= ~WSPF_DIRTY_ALL;
  645. }
  646. void
  647. Workspace::DeleteRegKey(BOOL Primary, ULONG Key, PTSTR Value)
  648. {
  649. HKEY RegKey = OpenKey(Primary, Key, FALSE);
  650. if (RegKey != NULL)
  651. {
  652. RegDeleteValue(RegKey, Value);
  653. RegCloseKey(RegKey);
  654. }
  655. }
  656. HRESULT
  657. Workspace::Flush(BOOL ForceSave, BOOL Cancellable)
  658. {
  659. if (getenv("WINDBG_NO_WORKSPACE_WINDOWS") != NULL)
  660. {
  661. // Window layout saving is suppressed so don't
  662. // consider them dirty.
  663. m_Flags &= ~WSPF_DIRTY_WINDOWS;
  664. }
  665. if ((m_Flags & WSPF_DIRTY_ALL) == 0 ||
  666. (g_QuietMode && !ForceSave))
  667. {
  668. return S_OK;
  669. }
  670. #ifdef DBG_WSP
  671. DebugPrint("Workspace dirty flags %X\n", m_Flags & WSPF_DIRTY_ALL);
  672. #endif
  673. WORD Str;
  674. PTSTR Arg;
  675. if (!strcmp(m_Value, g_WorkspaceDefaultName))
  676. {
  677. Arg = g_WorkspaceKeyNames[m_Key];
  678. if (m_Key == WSP_NAME_BASE)
  679. {
  680. Str = STR_Save_Base_Workspace;
  681. }
  682. else
  683. {
  684. Str = STR_Save_Default_Workspace;
  685. }
  686. }
  687. else
  688. {
  689. Str = STR_Save_Specific_Workspace;
  690. Arg = m_Value;
  691. }
  692. int Answer;
  693. if (ForceSave)
  694. {
  695. Answer = IDOK;
  696. }
  697. else
  698. {
  699. Answer = QuestionBox(Str, Cancellable ? MB_YESNOCANCEL : MB_YESNO,
  700. Arg);
  701. }
  702. if (Answer == IDNO)
  703. {
  704. return S_OK;
  705. }
  706. else if (Answer == IDCANCEL)
  707. {
  708. Assert(Cancellable);
  709. return S_FALSE;
  710. }
  711. if (m_Flags & WSPF_DIRTY_BREAKPOINTS)
  712. {
  713. UpdateBreakpointInformation();
  714. }
  715. if (m_Flags & WSPF_DIRTY_WINDOWS)
  716. {
  717. UpdateWindowInformation();
  718. }
  719. if (m_Flags & WSPF_DIRTY_LOG_FILE)
  720. {
  721. UpdateLogFileInformation();
  722. }
  723. if (m_Flags & WSPF_DIRTY_PATHS)
  724. {
  725. UpdatePathInformation();
  726. }
  727. if (m_Flags & WSPF_DIRTY_FILTERS)
  728. {
  729. UpdateFilterInformation();
  730. }
  731. if (m_Flags & WSPF_DIRTY_MRU_LIST)
  732. {
  733. UpdateMruListInformation();
  734. }
  735. return WriteReg();
  736. }
  737. WSP_ENTRY*
  738. Workspace::AllocateEntry(ULONG FullSize)
  739. {
  740. // Sizes must fit in USHORTs. This shouldn't be
  741. // a big problem since workspaces shouldn't have
  742. // huge data items in them.
  743. if (FullSize > 0xffff)
  744. {
  745. return NULL;
  746. }
  747. if (m_DataUsed + FullSize > m_DataLen)
  748. {
  749. ULONG NewLen = m_DataLen;
  750. do
  751. {
  752. NewLen += WSP_GROW_BY;
  753. }
  754. while (m_DataUsed + FullSize > NewLen);
  755. PUCHAR NewData = (PUCHAR)realloc(m_Data, NewLen);
  756. if (NewData == NULL)
  757. {
  758. return NULL;
  759. }
  760. m_Data = NewData;
  761. m_DataLen = NewLen;
  762. }
  763. WSP_ENTRY* Entry = (WSP_ENTRY*)(m_Data + m_DataUsed);
  764. m_DataUsed += FullSize;
  765. return Entry;
  766. }
  767. void
  768. Workspace::GetKeyName(ULONG Key, PSTR KeyName)
  769. {
  770. _tcscpy(KeyName, WSP_REG_KEY);
  771. if (Key > WSP_NAME_BASE)
  772. {
  773. _tcscat(KeyName, "\\");
  774. _tcscat(KeyName, g_WorkspaceKeyNames[Key]);
  775. }
  776. }
  777. HKEY
  778. Workspace::OpenKey(BOOL Primary, ULONG Key, BOOL Create)
  779. {
  780. TCHAR KeyName[MAX_PATH];
  781. HKEY RegKey;
  782. HKEY Base = Primary ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE;
  783. GetKeyName(Key, KeyName);
  784. if (Create)
  785. {
  786. if (RegCreateKeyEx(Base, KeyName, 0, NULL, 0,
  787. KEY_ALL_ACCESS, NULL, &RegKey,
  788. NULL) == ERROR_SUCCESS)
  789. {
  790. return RegKey;
  791. }
  792. }
  793. else if (RegOpenKeyEx(Base, KeyName, 0, KEY_ALL_ACCESS,
  794. &RegKey) == ERROR_SUCCESS)
  795. {
  796. return RegKey;
  797. }
  798. return NULL;
  799. }
  800. int
  801. Workspace::Apply(ULONG Flags)
  802. {
  803. WSP_ENTRY* Entry;
  804. ULONG Count;
  805. PUCHAR Data;
  806. BOOL UpdateColors = FALSE;
  807. int SessionStarts;
  808. ULONG MemWins = 0;
  809. #ifdef DBG_WSP
  810. DebugPrint("Applying workspace %s%s%s with:\n",
  811. m_Key == NULL ? "" : m_Key,
  812. m_Key == NULL ? "" : "\\",
  813. m_Value);
  814. #endif
  815. //
  816. // Scan for explicit session start entries first.
  817. // If any are present and a session is active
  818. // fail the apply before anything actually happens.
  819. //
  820. if ((Flags & (WSP_APPLY_AGAIN |
  821. WSP_APPLY_EXPLICIT)) == WSP_APPLY_EXPLICIT &&
  822. g_EngineThreadId)
  823. {
  824. Entry = NULL;
  825. while ((Entry = NextEntry(Entry)) != NULL)
  826. {
  827. switch(Entry->Tag)
  828. {
  829. case WSP_GLOBAL_EXE_COMMAND_LINE:
  830. case WSP_GLOBAL_DUMP_FILE_NAME:
  831. case WSP_GLOBAL_ATTACH_KERNEL_FLAGS:
  832. return -1;
  833. }
  834. }
  835. }
  836. SessionStarts = 0;
  837. Entry = NULL;
  838. while ((Entry = NextEntry(Entry)) != NULL)
  839. {
  840. #ifdef DBG_WSP
  841. DebugPrint(" %04X: Tag: %08X Size %X:%X\n",
  842. (PUCHAR)Entry - m_Data, Entry->Tag,
  843. Entry->DataSize, Entry->FullSize);
  844. #endif
  845. // If this is a reapply only a subset of the
  846. // workspace is applied to prevent duplication
  847. // and problems.
  848. if ((Flags & WSP_APPLY_AGAIN) &&
  849. Entry->Tag != WSP_GLOBAL_BREAKPOINTS &&
  850. Entry->Tag != WSP_GLOBAL_REGISTER_MAP)
  851. {
  852. continue;
  853. }
  854. if (WSP_TAG_GROUP(Entry->Tag) == WSP_GROUP_COLORS)
  855. {
  856. if (SetColor(WSP_TAG_ITEM(Entry->Tag),
  857. *WSP_ENTRY_DATA(COLORREF*, Entry)))
  858. {
  859. UpdateColors = TRUE;
  860. }
  861. continue;
  862. }
  863. switch(Entry->Tag)
  864. {
  865. case WSP_GLOBAL_SYMBOL_PATH:
  866. g_pUiSymbols->SetSymbolPath(WSP_ENTRY_DATA(PSTR, Entry));
  867. break;
  868. case WSP_GLOBAL_IMAGE_PATH:
  869. g_pUiSymbols->SetImagePath(WSP_ENTRY_DATA(PSTR, Entry));
  870. break;
  871. case WSP_GLOBAL_SOURCE_PATH:
  872. g_pUiSymbols->SetSourcePath(WSP_ENTRY_DATA(PSTR, Entry));
  873. break;
  874. case WSP_GLOBAL_WINDOW_OPTIONS:
  875. g_WinOptions = *WSP_ENTRY_DATA(PULONG, Entry);
  876. if (g_WinOptions & WOPT_AUTO_ARRANGE)
  877. {
  878. Arrange();
  879. }
  880. break;
  881. case WSP_GLOBAL_REGISTER_MAP:
  882. Count = Entry->DataSize / sizeof(*g_RegisterMap);
  883. g_RegisterMap = new USHORT[Count];
  884. if (g_RegisterMap != NULL)
  885. {
  886. memcpy(g_RegisterMap, WSP_ENTRY_DATA(PUSHORT, Entry),
  887. Count * sizeof(*g_RegisterMap));
  888. g_RegisterMapEntries = Count;
  889. }
  890. break;
  891. case WSP_GLOBAL_BREAKPOINTS:
  892. Assert(Entry->DataSize > 1);
  893. AddStringMultiCommand(UIC_INVISIBLE_EXECUTE,
  894. WSP_ENTRY_DATA(PSTR, Entry));
  895. break;
  896. case WSP_GLOBAL_LOG_FILE:
  897. Data = WSP_ENTRY_DATA(PUCHAR, Entry);
  898. g_pUiControl->OpenLogFile((PSTR)Data + sizeof(BOOL), *(PBOOL)Data);
  899. break;
  900. case WSP_GLOBAL_LOCAL_SOURCE_PATH:
  901. if (g_RemoteClient)
  902. {
  903. g_pUiLocSymbols->SetSourcePath(WSP_ENTRY_DATA(PSTR, Entry));
  904. }
  905. break;
  906. case WSP_GLOBAL_FILTERS:
  907. Assert(Entry->DataSize > 1);
  908. AddStringMultiCommand(UIC_INVISIBLE_EXECUTE,
  909. WSP_ENTRY_DATA(PSTR, Entry));
  910. break;
  911. case WSP_GLOBAL_FIXED_LOGFONT:
  912. g_Fonts[FONT_FIXED].LogFont = *WSP_ENTRY_DATA(LPLOGFONT, Entry);
  913. CreateIndexedFont(FONT_FIXED, TRUE);
  914. break;
  915. case WSP_GLOBAL_TAB_WIDTH:
  916. SetTabWidth(*WSP_ENTRY_DATA(PULONG, Entry));
  917. break;
  918. case WSP_GLOBAL_MRU_LIST:
  919. Data = WSP_ENTRY_DATA(PUCHAR, Entry);
  920. ReadMru(Data, Data + Entry->DataSize);
  921. break;
  922. case WSP_GLOBAL_REPEAT_COMMANDS:
  923. if (*WSP_ENTRY_DATA(PULONG, Entry))
  924. {
  925. g_pUiControl->
  926. RemoveEngineOptions(DEBUG_ENGOPT_NO_EXECUTE_REPEAT);
  927. }
  928. else
  929. {
  930. g_pUiControl->
  931. AddEngineOptions(DEBUG_ENGOPT_NO_EXECUTE_REPEAT);
  932. }
  933. break;
  934. case WSP_GLOBAL_COM_SETTINGS:
  935. if (Entry->DataSize <= sizeof(g_ComSettings))
  936. {
  937. memcpy(g_ComSettings, WSP_ENTRY_DATA(PSTR, Entry),
  938. Entry->DataSize);
  939. PrintAllocString(&g_KernelConnectOptions, 256,
  940. "com:port=%s,baud=%s", g_ComSettings,
  941. g_ComSettings + strlen(g_ComSettings) + 1);
  942. }
  943. break;
  944. case WSP_GLOBAL_1394_SETTINGS:
  945. if (Entry->DataSize <= sizeof(g_1394Settings))
  946. {
  947. memcpy(g_1394Settings, WSP_ENTRY_DATA(PSTR, Entry),
  948. Entry->DataSize);
  949. PrintAllocString(&g_KernelConnectOptions, 256,
  950. "1394:channel=%s", g_1394Settings);
  951. }
  952. break;
  953. case WSP_GLOBAL_DISASM_ACTIVATE_SOURCE:
  954. g_DisasmActivateSource = *WSP_ENTRY_DATA(PULONG, Entry);
  955. break;
  956. case WSP_GLOBAL_VIEW_TOOL_BAR:
  957. CheckMenuItem(g_hmenuMain, IDM_VIEW_TOOLBAR,
  958. *WSP_ENTRY_DATA(PULONG, Entry) ?
  959. MF_CHECKED : MF_UNCHECKED);
  960. Show_Toolbar(*WSP_ENTRY_DATA(PULONG, Entry));
  961. break;
  962. case WSP_GLOBAL_VIEW_STATUS_BAR:
  963. CheckMenuItem(g_hmenuMain, IDM_VIEW_STATUS,
  964. *WSP_ENTRY_DATA(PULONG, Entry) ?
  965. MF_CHECKED : MF_UNCHECKED);
  966. Show_StatusBar(*WSP_ENTRY_DATA(PULONG, Entry));
  967. break;
  968. case WSP_GLOBAL_AUTO_CMD_SCROLL:
  969. g_AutoCmdScroll = *WSP_ENTRY_DATA(PULONG, Entry);
  970. break;
  971. case WSP_GLOBAL_SRC_FILE_PATH:
  972. strcpy(g_SrcFilePath, WSP_ENTRY_DATA(PSTR, Entry));
  973. break;
  974. case WSP_GLOBAL_EXE_COMMAND_LINE:
  975. if ((Flags & WSP_APPLY_EXPLICIT) &&
  976. DupAllocString(&g_DebugCommandLine,
  977. WSP_ENTRY_DATA(PSTR, Entry)))
  978. {
  979. SessionStarts++;
  980. }
  981. break;
  982. case WSP_GLOBAL_EXE_CREATE_FLAGS:
  983. g_DebugCreateFlags = *WSP_ENTRY_DATA(PULONG, Entry);
  984. break;
  985. case WSP_GLOBAL_DUMP_FILE_NAME:
  986. if ((Flags & WSP_APPLY_EXPLICIT) &&
  987. DupAllocString(&g_DumpFile,
  988. WSP_ENTRY_DATA(PSTR, Entry)))
  989. {
  990. SessionStarts++;
  991. }
  992. break;
  993. case WSP_GLOBAL_ATTACH_KERNEL_FLAGS:
  994. if ((Flags & WSP_APPLY_EXPLICIT))
  995. {
  996. g_AttachKernelFlags = *WSP_ENTRY_DATA(PULONG, Entry);
  997. SessionStarts++;
  998. }
  999. break;
  1000. case WSP_GLOBAL_TYPE_OPTIONS:
  1001. {
  1002. g_TypeOptions = *WSP_ENTRY_DATA(PULONG, Entry);
  1003. if (g_pUiSymbols2 != NULL)
  1004. {
  1005. g_pUiSymbols2->SetTypeOptions(g_TypeOptions);
  1006. }
  1007. }
  1008. break;
  1009. case WSP_GLOBAL_DUMP_FILE_PATH:
  1010. strcpy(g_DumpFilePath, WSP_ENTRY_DATA(PSTR, Entry));
  1011. break;
  1012. case WSP_GLOBAL_EXE_FILE_PATH:
  1013. strcpy(g_ExeFilePath, WSP_ENTRY_DATA(PSTR, Entry));
  1014. break;
  1015. case WSP_WINDOW_COMMONWIN_1:
  1016. WSP_COMMONWIN_HEADER* Hdr;
  1017. HWND Win;
  1018. PCOMMONWIN_DATA WinData;
  1019. Hdr = WSP_ENTRY_DATA(WSP_COMMONWIN_HEADER*, Entry);
  1020. Win = New_OpenDebugWindow(Hdr->Type, TRUE, MemWins);
  1021. if (Win != NULL &&
  1022. (WinData = GetCommonWinData(Win)) != NULL &&
  1023. Entry->DataSize > sizeof(WSP_COMMONWIN_HEADER))
  1024. {
  1025. Data = (PUCHAR)(Hdr + 1);
  1026. WinData->m_InAutoOp++;
  1027. WinData->ApplyWorkspace1(Data, Data +
  1028. (Entry->DataSize -
  1029. sizeof(WSP_COMMONWIN_HEADER)));
  1030. WinData->m_InAutoOp--;
  1031. }
  1032. // A user can have as many open memory windows as
  1033. // they like, which makes things a little tricky
  1034. // for workspaces as applying stacked workspaces
  1035. // could result in memory windows multiplying out
  1036. // of control if the same set of memory windows
  1037. // is saved in each workspace level. To avoid
  1038. // this and to function more like the other windows
  1039. // we reuse memory windows from any that are
  1040. // already in existence.
  1041. if (Hdr->Type == MEM_WINDOW)
  1042. {
  1043. MemWins++;
  1044. }
  1045. break;
  1046. case WSP_WINDOW_FRAME_PLACEMENT:
  1047. LPWINDOWPLACEMENT Place;
  1048. Place = WSP_ENTRY_DATA(LPWINDOWPLACEMENT, Entry);
  1049. SetWindowPlacement(g_hwndFrame, Place);
  1050. break;
  1051. case WSP_WINDOW_FRAME_TITLE:
  1052. SetTitleExplicitText(WSP_ENTRY_DATA(PSTR, Entry));
  1053. break;
  1054. }
  1055. }
  1056. if (UpdateColors)
  1057. {
  1058. UpdateAllColors();
  1059. }
  1060. return SessionStarts;
  1061. }
  1062. HRESULT
  1063. UiSwitchWorkspace(ULONG Key, PTSTR Value, BOOL Create,
  1064. ULONG Flags, int* SessionStarts)
  1065. {
  1066. if (getenv("WINDBG_NO_WORKSPACE") != NULL)
  1067. {
  1068. return E_NOTIMPL;
  1069. }
  1070. HRESULT Status;
  1071. Workspace* OldWsp;
  1072. Workspace* NewWsp;
  1073. int Starts = 0;
  1074. Status = Workspace::Read(Key, Value, &NewWsp);
  1075. if (Status != S_OK)
  1076. {
  1077. if (Status == E_NOINTERFACE && Create)
  1078. {
  1079. // Workspace does not exist so create a new one.
  1080. Status = Workspace::Create(Key, Value, &NewWsp);
  1081. }
  1082. if (Status != S_OK)
  1083. {
  1084. return Status;
  1085. }
  1086. }
  1087. // We have a new workspace ready to go so flush the old one.
  1088. OldWsp = g_Workspace;
  1089. if (OldWsp != NULL)
  1090. {
  1091. OldWsp->Flush(FALSE, FALSE);
  1092. }
  1093. // Apply the new workspace with no global workspace to
  1094. // avoid writing changes into the workspace as we apply it.
  1095. g_Workspace = NULL;
  1096. if (NewWsp != NULL)
  1097. {
  1098. Starts = NewWsp->Apply(Flags);
  1099. // Clear any window messages queued during the workspace
  1100. // application so that they're processed with no
  1101. // active workspace.
  1102. ProcessPendingMessages();
  1103. }
  1104. if (SessionStarts != NULL)
  1105. {
  1106. *SessionStarts = Starts;
  1107. }
  1108. if (Starts < 0)
  1109. {
  1110. // Apply failed so put the old workspace back.
  1111. g_Workspace = OldWsp;
  1112. return E_FAIL;
  1113. }
  1114. else
  1115. {
  1116. // Apply succeeded to replace the old workspace.
  1117. g_Workspace = NewWsp;
  1118. delete OldWsp;
  1119. return S_OK;
  1120. }
  1121. }
  1122. HRESULT
  1123. UiDelayedSwitchWorkspace(void)
  1124. {
  1125. Assert(!g_WspSwitchBufferAvailable);
  1126. HRESULT Status = UiSwitchWorkspace(g_WspSwitchKey, g_WspSwitchValue, TRUE,
  1127. WSP_APPLY_DEFAULT, NULL);
  1128. // Mark the delayed switch buffer as available and
  1129. // wait for acknowledgement.
  1130. g_WspSwitchBufferAvailable = TRUE;
  1131. while (g_WspSwitchValue[0])
  1132. {
  1133. Sleep(50);
  1134. }
  1135. return Status;
  1136. }
  1137. void
  1138. EngSwitchWorkspace(ULONG Key, PTSTR Value)
  1139. {
  1140. // If the user explicitly selected a workspace
  1141. // don't override it due to engine activity.
  1142. if (g_ExplicitWorkspace ||
  1143. g_Exit)
  1144. {
  1145. return;
  1146. }
  1147. // We can't switch workspaces on the engine thread
  1148. // because of the UI work involved. Send the
  1149. // switch over to the UI thread and wait for
  1150. // it to be processed.
  1151. Assert(g_WspSwitchBufferAvailable);
  1152. g_WspSwitchBufferAvailable = FALSE;
  1153. g_WspSwitchKey = Key;
  1154. _tcscpy(g_WspSwitchValue, Value);
  1155. PostMessage(g_hwndFrame, WU_SWITCH_WORKSPACE, 0, 0);
  1156. if (g_pDbgClient != NULL)
  1157. {
  1158. // Temporarily disable event callbacks to keep
  1159. // activity at a minimum while we're in this halfway state.
  1160. g_pDbgClient->SetEventCallbacks(NULL);
  1161. while (!g_WspSwitchBufferAvailable)
  1162. {
  1163. if (FAILED(g_pDbgClient->DispatchCallbacks(50)))
  1164. {
  1165. Sleep(50);
  1166. }
  1167. }
  1168. g_pDbgClient->SetEventCallbacks(&g_EventCb);
  1169. }
  1170. else
  1171. {
  1172. while (!g_WspSwitchBufferAvailable)
  1173. {
  1174. Sleep(100);
  1175. }
  1176. }
  1177. // We know that at this point the new workspace cannot be dirty
  1178. // so just clear the dirty flags.
  1179. if (g_Workspace)
  1180. {
  1181. g_Workspace->ClearDirty();
  1182. }
  1183. // Let the UI thread continue.
  1184. g_WspSwitchKey = WSP_NAME_BASE;
  1185. g_WspSwitchValue[0] = 0;
  1186. Sleep(50);
  1187. //
  1188. // Warn the user is the workspace was not be created properly.
  1189. //
  1190. if (!g_Workspace)
  1191. {
  1192. InformationBox(ERR_NULL_Workspace, NULL);
  1193. return;
  1194. }
  1195. }
  1196. PSTR g_WspGlobalNames[] =
  1197. {
  1198. "Symbol path", "Image path", "Source path", "Window menu checks",
  1199. "Register customization", "Breakpoints", "Log file settings",
  1200. "Local source path", "Event filter settings", "Fixed-width font",
  1201. "Tab width", "MRU list", "Repeat commands setting", "COM port settings",
  1202. "1394 settings", "Activate source windows in disassembly mode",
  1203. "Show tool bar", "Show status bar", "Automatically scroll command window",
  1204. "Source open dialog path", "Executable command line",
  1205. "Executable create flags", "Dump file name", "Kernel attach flags",
  1206. "Type options", "Dump open dialog path", "Executable open dialog path",
  1207. };
  1208. PSTR g_WspWindowNames[] =
  1209. {
  1210. "Child window settings", "WinDBG window settings", "WinDBG window title",
  1211. };
  1212. PSTR
  1213. GetWspTagName(WSP_TAG Tag)
  1214. {
  1215. ULONG Item = WSP_TAG_ITEM(Tag);
  1216. static char Buffer[128];
  1217. switch(WSP_TAG_GROUP(Tag))
  1218. {
  1219. case WSP_GROUP_GLOBAL:
  1220. if (Item < WSP_GLOBAL_COUNT)
  1221. {
  1222. return g_WspGlobalNames[Item];
  1223. }
  1224. break;
  1225. case WSP_GROUP_WINDOW:
  1226. if (Item < WSP_WINDOW_COUNT)
  1227. {
  1228. return g_WspWindowNames[Item];
  1229. }
  1230. break;
  1231. case WSP_GROUP_COLORS:
  1232. INDEXED_COLOR* IdxCol = GetIndexedColor(Item);
  1233. if (IdxCol != NULL)
  1234. {
  1235. sprintf(Buffer, "%s color", IdxCol->Name);
  1236. return Buffer;
  1237. }
  1238. break;
  1239. }
  1240. return "Unknown tag";
  1241. }