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.

1631 lines
40 KiB

  1. /*++
  2. Copyright (c) 1999-2001 Microsoft Corporation
  3. Module Name:
  4. util.cpp
  5. --*/
  6. #include "precomp.hxx"
  7. #pragma hdrstop
  8. //Current Help Id for Open, Merge, Save and Open project dialog box
  9. WORD g_CurHelpId;
  10. // Number of dialog/message boxes currently open
  11. int g_nBoxCount;
  12. BOOL g_fNoPopups;
  13. HWND
  14. MDIGetActive(
  15. HWND hwndParent,
  16. BOOL *lpbMaximized
  17. )
  18. /*++
  19. Routine Description:
  20. Create the command window.
  21. Arguments:
  22. hwndParent - The parent window to the command window. In an MDI document,
  23. this is usually the handle to the MDI client window: g_hwndMDIClient
  24. Return Value:
  25. The return value is the handle to the active MDI child window.
  26. NULL if no MDI window has been created.
  27. --*/
  28. {
  29. Assert(IsWindow(hwndParent));
  30. return (HWND)SendMessage(hwndParent,
  31. WM_MDIGETACTIVE,
  32. 0,
  33. (LPARAM)lpbMaximized
  34. );
  35. }
  36. /*** hGetBoxParent
  37. **
  38. ** Synopsis:
  39. ** hwnd = hGetBoxParent()
  40. **
  41. ** Entry:
  42. ** none
  43. **
  44. ** Returns:
  45. **
  46. ** Description:
  47. ** Gets a suitable parent window handle for an
  48. ** invocation of a message or dialog box.
  49. ** Helper function to util.c functions so declared
  50. ** near.
  51. **
  52. */
  53. HWND
  54. hGetBoxParent()
  55. {
  56. HWND hCurWnd;
  57. int i = 0;
  58. hCurWnd = GetFocus();
  59. if (hCurWnd)
  60. {
  61. while (GetWindowLong(hCurWnd, GWL_STYLE) & WS_CHILD)
  62. {
  63. hCurWnd = GetParent(hCurWnd);
  64. Dbg((++i < 100));
  65. }
  66. }
  67. else
  68. {
  69. hCurWnd = g_hwndFrame;
  70. }
  71. return hCurWnd;
  72. }
  73. /****************************************************************************
  74. FUNCTION: MsgBox
  75. PURPOSE: General purpose message box routine which takes
  76. a pointer to the message text. Provides
  77. program title as caption.
  78. ****************************************************************************/
  79. int
  80. MsgBox(
  81. HWND hwndParent,
  82. PTSTR szText,
  83. UINT wType
  84. )
  85. /*++
  86. Routine Description:
  87. Generial purpose message box routine which takes a pointer to a message
  88. text and prvoides the program title for the caption of the message box.
  89. Arguments:
  90. hwndParament - Supplies the parent window handle for the message box
  91. szText - Supplies a pointer to the message box text.
  92. wType - Supplies the message box type (to specify buttons)
  93. Return Value:
  94. Returns the message box return code
  95. --*/
  96. {
  97. int MsgBoxRet = IDOK;
  98. if (g_fNoPopups)
  99. {
  100. //
  101. // log the string to the command win in case testing
  102. // or when the remote server is running
  103. //
  104. CmdLogFmt (_T("%s\r\n"), szText);
  105. }
  106. else
  107. {
  108. g_nBoxCount++;
  109. MsgBoxRet = MessageBox(hwndParent, szText,
  110. g_MainTitleText, wType);
  111. g_nBoxCount--;
  112. }
  113. return MsgBoxRet;
  114. } /* MsgBox() */
  115. /*** ErrorBox
  116. **
  117. ** Returns:
  118. ** FALSE
  119. **
  120. ** Description:
  121. ** Display an error message box with an "Error" title, an OK
  122. ** button and a Exclamation Icon. First parameter is a
  123. ** reference string in the ressource file. The string
  124. ** can contain printf formatting chars, the arguments
  125. ** follow from the second parameter onwards.
  126. **
  127. */
  128. BOOL
  129. ErrorBox(
  130. HWND hwnd,
  131. UINT type,
  132. int wErrorFormat,
  133. ...
  134. )
  135. {
  136. TCHAR szErrorFormat[MAX_MSG_TXT];
  137. TCHAR szErrorText[MAX_VAR_MSG_TXT]; // size is as big as considered necessary
  138. va_list vargs;
  139. // load format string from resource file
  140. Dbg(LoadString(g_hInst, wErrorFormat, (PTSTR)szErrorFormat, MAX_MSG_TXT));
  141. va_start(vargs, wErrorFormat);
  142. _vstprintf(szErrorText, szErrorFormat, vargs);
  143. va_end(vargs);
  144. if (hwnd == NULL)
  145. {
  146. hwnd = g_hwndFrame;
  147. }
  148. if (type == 0)
  149. {
  150. type = MB_TASKMODAL;
  151. }
  152. MsgBox(g_hwndFrame, (PTSTR)szErrorText, type | MB_OK | MB_ICONINFORMATION);
  153. return FALSE; //Keep it always FALSE please
  154. }
  155. /*** InformationBox
  156. **
  157. ** Description:
  158. ** Display an information message box with an "Information"
  159. ** title, an OK button and an Information Icon.
  160. **
  161. */
  162. void
  163. InformationBox(
  164. WORD wDescript
  165. ...
  166. )
  167. {
  168. TCHAR szFormat[MAX_MSG_TXT];
  169. TCHAR szText[MAX_VAR_MSG_TXT]; // size is as big as considered necessary
  170. va_list vargs;
  171. // load format string from resource file
  172. Dbg(LoadString(g_hInst, wDescript, (PTSTR)szFormat, MAX_MSG_TXT));
  173. // set up szText from passed parameters
  174. va_start(vargs, wDescript);
  175. _vstprintf(szText, szFormat, vargs);
  176. va_end(vargs);
  177. MsgBox(g_hwndFrame, (PTSTR)szText, MB_OK | MB_ICONINFORMATION | MB_TASKMODAL);
  178. return;
  179. }
  180. /*** QuestionBox
  181. **
  182. ** Synopsis:
  183. ** int = QuestionBox(wCaptionId, wMsgFormat, wType, ...)
  184. **
  185. ** Entry:
  186. **
  187. ** Returns:
  188. ** The result of the message box call
  189. **
  190. ** Description:
  191. ** Display an query box with combination of YES, NO and
  192. ** CANCEL buttons and a question mark Icon.
  193. ** See ErrorBox for discussion.
  194. **
  195. */
  196. int
  197. CDECL
  198. QuestionBox(
  199. WORD wMsgFormat,
  200. UINT wType,
  201. ...
  202. )
  203. {
  204. TCHAR szMsgFormat[MAX_MSG_TXT];
  205. TCHAR szMsgText[MAX_VAR_MSG_TXT];
  206. va_list vargs;
  207. //Load format string from resource file
  208. Dbg(LoadString(g_hInst, wMsgFormat, (PTSTR)szMsgFormat, MAX_MSG_TXT));
  209. //Set up szMsgText from passed parameters
  210. va_start(vargs, wType);
  211. _vstprintf(szMsgText, szMsgFormat, vargs);
  212. va_end(vargs);
  213. return MsgBox(g_hwndFrame, szMsgText,
  214. wType | MB_ICONEXCLAMATION | MB_TASKMODAL);
  215. } /* QuestionBox() */
  216. /****************************************************************************
  217. FUNCTION: QuestionBox2
  218. PURPOSE: Display an query box with combination of YES, NO and
  219. CANCEL buttons and a question mark Icon. The type and
  220. the parent window are adjustable.
  221. RETURNS: MessageBox result
  222. ****************************************************************************/
  223. int
  224. CDECL
  225. QuestionBox2(
  226. HWND hwnd,
  227. WORD wMsgFormat,
  228. UINT wType,
  229. ...
  230. )
  231. {
  232. TCHAR szMsgFormat[MAX_MSG_TXT];
  233. TCHAR szMsgText[MAX_VAR_MSG_TXT];
  234. va_list vargs;
  235. //Load format string from resource file
  236. Dbg(LoadString(g_hInst, wMsgFormat, (PTSTR)szMsgFormat, MAX_MSG_TXT));
  237. //Set up szMsgText from passed parameters
  238. va_start(vargs, wType);
  239. _vstprintf(szMsgText, szMsgFormat, vargs);
  240. va_end(vargs);
  241. return MsgBox(hwnd, szMsgText, wType | MB_ICONEXCLAMATION);
  242. } /* QuestionBox2() */
  243. /*** ShowAssert
  244. **
  245. ** Synopsis:
  246. ** void = ShowAssert(szCond, iLine, szFile)
  247. **
  248. ** Entry:
  249. ** szCond - tokenized form of the failed condition
  250. ** iLine - Line number for the assertion
  251. ** szFile - File for the assertion
  252. **
  253. ** Returns:
  254. ** void
  255. **
  256. ** Description:
  257. ** Prepare and display a Message Box with szCondition, iLine and
  258. ** szFile as fields.
  259. **
  260. */
  261. void
  262. ShowAssert(
  263. PTSTR condition,
  264. UINT line,
  265. PTSTR file
  266. )
  267. {
  268. TCHAR text[MAX_VAR_MSG_TXT];
  269. //Build line, show assertion and exit program
  270. _stprintf(text, _T("- Line:%u, File:%Fs, Condition:%Fs"),
  271. (WPARAM) line, file, condition);
  272. TCHAR szBuffer[_MAX_PATH];
  273. PTSTR pszBuffer;
  274. PTSTR szAssertFile = _T("assert.wbg");
  275. HANDLE hFile = NULL;
  276. hFile = CreateFile(szAssertFile,
  277. GENERIC_WRITE,
  278. 0,
  279. NULL,
  280. CREATE_ALWAYS,
  281. FILE_ATTRIBUTE_NORMAL,
  282. NULL
  283. );
  284. if (INVALID_HANDLE_VALUE != hFile)
  285. {
  286. // Write the text out to the file
  287. DWORD dwBytesWritten = 0;
  288. Assert(WriteFile(hFile, text, _tcslen(text), &dwBytesWritten, NULL));
  289. Assert(_tcslen(text) == dwBytesWritten);
  290. CloseHandle(hFile);
  291. }
  292. int Action =
  293. MessageBox(GetDesktopWindow(), text, "Assertion Failure",
  294. MB_ABORTRETRYIGNORE);
  295. if (Action == IDABORT)
  296. {
  297. exit(3);
  298. }
  299. else if (Action == IDRETRY)
  300. {
  301. DebugBreak();
  302. }
  303. } // ShowAssert()
  304. /*** StartDialog
  305. **
  306. ** Synopsis:
  307. ** int = StartDialog(rcDlgNb, dlgProc, lParam)
  308. **
  309. ** Entry:
  310. ** rcDlgNb - Resource number of dialog to be openned
  311. ** dlgProc - Filter procedure for the dialog
  312. ** lParam - Data passed into the dlg proc via LPARAM
  313. **
  314. ** Returns:
  315. ** Result of the dialog box call
  316. **
  317. ** Description:
  318. ** Loads and execute the dialog box 'rcDlgNb' (resource
  319. ** file string number) associated with the dialog
  320. ** function 'dlgProc'
  321. **
  322. */
  323. int
  324. StartDialog(
  325. int rcDlgNb,
  326. DLGPROC dlgProc,
  327. LPARAM lParam
  328. )
  329. {
  330. LRESULT result;
  331. //
  332. //Execute Dialog Box
  333. //
  334. g_nBoxCount++;
  335. result = DialogBoxParam(g_hInst,
  336. MAKEINTRESOURCE(rcDlgNb),
  337. hGetBoxParent(),
  338. dlgProc,
  339. lParam
  340. );
  341. Assert(result != (LRESULT)-1);
  342. g_nBoxCount--;
  343. return (int)result;
  344. }
  345. void
  346. ProcessNonDlgMessage(LPMSG Msg)
  347. {
  348. #if 0
  349. {
  350. DebugPrint("NonDlg msg %X for %p, args %X %X\n",
  351. Msg->message, Msg->hwnd, Msg->wParam, Msg->lParam);
  352. }
  353. #endif
  354. // If a keyboard message is for the MDI , let the MDI client
  355. // take care of it. Otherwise, check to see if it's a normal
  356. // accelerator key (like F3 = find next). Otherwise, just handle
  357. // the message as usual.
  358. if (!TranslateMDISysAccel(g_hwndMDIClient, Msg) &&
  359. !TranslateAccelerator(g_hwndFrame, g_hMainAccTable, Msg))
  360. {
  361. //
  362. // If this is a right-button-down over a child window,
  363. // automatically activate the window's contex menu.
  364. //
  365. if (Msg->message == WM_RBUTTONDOWN &&
  366. IsChild(g_hwndMDIClient, Msg->hwnd))
  367. {
  368. HMENU Menu;
  369. PCOMMONWIN_DATA CmnWin;
  370. POINT ScreenPt;
  371. POINT Pt = {LOWORD(Msg->lParam), HIWORD(Msg->lParam)};
  372. ClientToScreen(Msg->hwnd, &Pt);
  373. ScreenPt = Pt;
  374. ScreenToClient(g_hwndMDIClient, &Pt);
  375. HWND Win = ChildWindowFromPointEx(g_hwndMDIClient, Pt,
  376. CWP_SKIPINVISIBLE);
  377. if (Win != NULL &&
  378. (CmnWin = GetCommonWinData(Win)) != NULL &&
  379. (Menu = CmnWin->GetContextMenu()) != NULL)
  380. {
  381. UINT Item = TrackPopupMenu(Menu, TPM_LEFTALIGN | TPM_TOPALIGN |
  382. TPM_NONOTIFY | TPM_RETURNCMD |
  383. TPM_RIGHTBUTTON,
  384. ScreenPt.x, ScreenPt.y,
  385. 0, Msg->hwnd, NULL);
  386. if (Item)
  387. {
  388. CmnWin->OnContextMenuSelection(Item);
  389. }
  390. return;
  391. }
  392. }
  393. TranslateMessage(Msg);
  394. DispatchMessage(Msg);
  395. }
  396. }
  397. void
  398. ProcessPendingMessages(void)
  399. {
  400. MSG Msg;
  401. // Process all available messages.
  402. while (PeekMessage(&Msg, NULL, 0, 0, PM_NOREMOVE))
  403. {
  404. if (!GetMessage(&Msg, NULL, 0, 0))
  405. {
  406. g_Exit = TRUE;
  407. break;
  408. }
  409. if (g_FindDialog == NULL ||
  410. !IsDialogMessage(g_FindDialog, &Msg))
  411. {
  412. ProcessNonDlgMessage(&Msg);
  413. }
  414. }
  415. }
  416. /****************************************************************************
  417. FUNCTION: InfoBox
  418. PURPOSE: Opens a Dialog box with a title and accepting
  419. a printf style for text. It's for DEBUGGING USE ONLY
  420. ****************************************************************************/
  421. int
  422. InfoBox(
  423. PTSTR text,
  424. ...
  425. )
  426. {
  427. TCHAR buffer[MAX_MSG_TXT];
  428. va_list vargs;
  429. va_start(vargs, text);
  430. _vstprintf(buffer, text, vargs);
  431. va_end(vargs);
  432. return MsgBox(GetActiveWindow(), buffer, MB_OK | MB_ICONINFORMATION | MB_TASKMODAL);
  433. }
  434. void
  435. ExitDebugger(PDEBUG_CLIENT Client, ULONG Code)
  436. {
  437. if (Client != NULL && !g_RemoteClient)
  438. {
  439. Client->EndSession(DEBUG_END_PASSIVE);
  440. // Force servers to get cleaned up.
  441. Client->EndSession(DEBUG_END_REENTRANT);
  442. }
  443. ExitProcess(Code);
  444. }
  445. // XXX drewb - Is this functionality present in other utilities?
  446. // FatalErrorBox is close. Probably can combine something here.
  447. void
  448. ErrorExit(PDEBUG_CLIENT Client, PCSTR Format, ...)
  449. {
  450. char Message[1024];
  451. va_list Args;
  452. va_start(Args, Format);
  453. _vsnprintf(Message, sizeof(Message), Format, Args);
  454. va_end(Args);
  455. // XXX drewb - Could put up message box.
  456. OutputDebugString(Message);
  457. ExitDebugger(Client, E_FAIL);
  458. }
  459. #define MAX_FORMAT_STRINGS 8
  460. LPSTR
  461. FormatAddr64(
  462. ULONG64 addr
  463. )
  464. /*++
  465. Routine Description:
  466. Format a 64 bit address, showing the high bits or not
  467. according to various flags.
  468. An array of static string buffers is used, returning a different
  469. buffer for each successive call so that it may be used multiple
  470. times in the same printf.
  471. Arguments:
  472. addr - Supplies the value to format
  473. Return Value:
  474. A pointer to the string buffer containing the formatted number
  475. --*/
  476. {
  477. static CHAR strings[MAX_FORMAT_STRINGS][18];
  478. static int next = 0;
  479. LPSTR string;
  480. string = strings[next];
  481. ++next;
  482. if (next >= MAX_FORMAT_STRINGS) {
  483. next = 0;
  484. }
  485. if (g_Ptr64) {
  486. sprintf(string, "%08x`%08x", (ULONG)(addr>>32), (ULONG)addr);
  487. } else {
  488. sprintf(string, "%08x", (ULONG)addr);
  489. }
  490. return string;
  491. }
  492. static BOOL FAddToSearchPath = FALSE;
  493. static BOOL FAddToRootMap = FALSE;
  494. /*
  495. ** AppendFilter
  496. **
  497. ** Description:
  498. ** Append a filter to an existing filters string.
  499. **
  500. */
  501. BOOL
  502. AppendFilter(
  503. WORD filterTextId,
  504. int filterExtId,
  505. PTSTR filterString,
  506. int *len,
  507. int maxLen
  508. )
  509. {
  510. int size;
  511. TCHAR Tmp[MAX_MSG_TXT];
  512. //
  513. // Append filter text
  514. //
  515. Dbg(LoadString(g_hInst, filterTextId, Tmp, MAX_MSG_TXT));
  516. size = _tcslen(Tmp) + 1;
  517. if (*len + size > maxLen)
  518. {
  519. return FALSE;
  520. }
  521. memmove(filterString + *len, Tmp, size);
  522. *len += size;
  523. //
  524. // Append filter extension
  525. //
  526. Dbg(LoadString(g_hInst, filterExtId, Tmp, MAX_MSG_TXT));
  527. size = _tcslen(Tmp) + 1;
  528. if (*len + size > maxLen)
  529. {
  530. return FALSE;
  531. }
  532. memmove(filterString + *len, Tmp, size);
  533. *len += size;
  534. return TRUE;
  535. }
  536. /*
  537. ** InitFilterString
  538. **
  539. ** Description:
  540. ** Initialize file filters for file dialog boxes.
  541. */
  542. void
  543. InitFilterString(
  544. WORD titleId,
  545. PTSTR filter,
  546. int maxLen
  547. )
  548. {
  549. int len = 0;
  550. switch (titleId) {
  551. case DLG_Browse_CrashDump_Title:
  552. AppendFilter(TYP_File_DUMP, DEF_Ext_DUMP, filter, &len, maxLen);
  553. break;
  554. case DLG_Browse_Executable_Title:
  555. AppendFilter(TYP_File_EXE, DEF_Ext_EXE, filter, &len, maxLen);
  556. break;
  557. case DLG_Browse_LogFile_Title:
  558. AppendFilter(TYP_File_LOG, DEF_Ext_LOG, filter, &len, maxLen);
  559. break;
  560. case DLG_Open_Filebox_Title:
  561. case DLG_Browse_Filebox_Title:
  562. AppendFilter(TYP_File_SOURCE, DEF_Ext_SOURCE, filter, &len, maxLen);
  563. AppendFilter(TYP_File_INCLUDE, DEF_Ext_INCLUDE, filter, &len, maxLen);
  564. AppendFilter(TYP_File_ASMSRC, DEF_Ext_ASMSRC, filter, &len, maxLen);
  565. AppendFilter(TYP_File_INC, DEF_Ext_INC, filter, &len, maxLen);
  566. AppendFilter(TYP_File_RC, DEF_Ext_RC, filter, &len, maxLen);
  567. AppendFilter(TYP_File_DLG, DEF_Ext_DLG, filter, &len, maxLen);
  568. AppendFilter(TYP_File_DEF, DEF_Ext_DEF, filter, &len, maxLen);
  569. AppendFilter(TYP_File_MAK, DEF_Ext_MAK, filter, &len, maxLen);
  570. break ;
  571. case DLG_Browse_DbugDll_Title:
  572. AppendFilter(TYP_File_DLL, DEF_Ext_DLL, filter, &len, maxLen);
  573. break;
  574. default:
  575. Assert(FALSE);
  576. break;
  577. }
  578. AppendFilter(TYP_File_ALL, DEF_Ext_ALL, filter, &len, maxLen);
  579. filter[len] = _T('\0');
  580. }
  581. BOOL
  582. StartFileDlg(
  583. HWND hwnd,
  584. int titleId,
  585. int defExtId,
  586. int helpId,
  587. int templateId,
  588. PTSTR InitialDir,
  589. PTSTR fileName,
  590. DWORD* pFlags,
  591. LPOFNHOOKPROC lpfnHook
  592. )
  593. /*++
  594. Routine Description:
  595. This function is used by windbg to open the set of common file handling
  596. dialog boxes.
  597. Arguments:
  598. hwnd - Supplies the wnd to hook the dialog box to
  599. titleId - Supplies the string resource of the title
  600. defExtId - Supplies The default extension resource string
  601. helpId - Supplies the help number for the dialog box
  602. templateId - Supplies the dialog resource number if non-zero
  603. fileName - Supplies the default file name
  604. pFiles - Supplies a pointer to flags
  605. lpfnHook - Supplies the address of a hook procedure for the dialog
  606. Return Value:
  607. The result of the dialog box call (usually TRUE for OK and FALSE for
  608. cancel)
  609. --*/
  610. {
  611. #define filtersMaxSize 350
  612. OPENFILENAME_NT4 OpenFileName = {0};
  613. TCHAR title[MAX_MSG_TXT];
  614. TCHAR defExt[MAX_MSG_TXT];
  615. BOOL result;
  616. TCHAR filters[filtersMaxSize];
  617. LPOFNHOOKPROC lpDlgHook = NULL;
  618. HCURSOR hSaveCursor;
  619. TCHAR files[_MAX_PATH + 8];
  620. TCHAR szExt[_MAX_EXT + 8];
  621. TCHAR szBase[_MAX_PATH + 8];
  622. int indx;
  623. TCHAR ch;
  624. TCHAR fname[_MAX_FNAME];
  625. TCHAR ext[_MAX_EXT];
  626. PTSTR LocalInitialDir = NULL;
  627. *pFlags |= (OFN_EXPLORER | OFN_NOCHANGEDIR);
  628. if (InitialDir == NULL || !InitialDir[0])
  629. {
  630. DWORD retval = GetCurrentDirectory(NULL, NULL);
  631. InitialDir = (PTSTR)calloc(retval, sizeof(TCHAR) );
  632. if (InitialDir == NULL)
  633. {
  634. return FALSE;
  635. }
  636. GetCurrentDirectory(retval, InitialDir);
  637. LocalInitialDir = InitialDir;
  638. }
  639. if (DLG_Browse_Filebox_Title == titleId)
  640. {
  641. _tsplitpath( fileName, NULL, NULL, fname, ext );
  642. _tmakepath( files, NULL, NULL, fname, ext );
  643. }
  644. else
  645. {
  646. _tcscpy(files, fileName);
  647. }
  648. //
  649. // Set the Hour glass cursor
  650. //
  651. hSaveCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
  652. InitFilterString((WORD)titleId, (PTSTR)filters, (int)filtersMaxSize);
  653. Dbg(LoadString(g_hInst, titleId, (PTSTR)title, MAX_MSG_TXT));
  654. Dbg(LoadString(g_hInst, defExtId, (PTSTR)defExt, MAX_MSG_TXT));
  655. if (templateId)
  656. {
  657. //
  658. // Build dialog box Name
  659. //
  660. *pFlags |= OFN_ENABLETEMPLATE;
  661. OpenFileName.lpTemplateName = MAKEINTRESOURCE(templateId);
  662. }
  663. else
  664. {
  665. *pFlags |= OFN_EXPLORER;
  666. }
  667. //
  668. // Make instance for _T('dlgProc')
  669. //
  670. if (lpfnHook)
  671. {
  672. lpDlgHook = lpfnHook;
  673. *pFlags |= OFN_ENABLEHOOK;
  674. }
  675. g_CurHelpId = (WORD) helpId;
  676. OpenFileName.lStructSize = sizeof(OpenFileName);
  677. OpenFileName.hwndOwner = hwnd;
  678. OpenFileName.hInstance = g_hInst;
  679. OpenFileName.lpstrFilter = (PTSTR)filters;
  680. OpenFileName.lpstrCustomFilter = NULL;
  681. OpenFileName.nMaxCustFilter = 0;
  682. OpenFileName.nFilterIndex = 1;
  683. OpenFileName.lpstrFile = files;
  684. OpenFileName.nMaxFile = _MAX_PATH;
  685. OpenFileName.lpstrFileTitle = NULL;
  686. OpenFileName.lpstrInitialDir = InitialDir;
  687. OpenFileName.lpstrTitle = (PTSTR)title;
  688. OpenFileName.Flags = *pFlags;
  689. OpenFileName.lpstrDefExt = (PTSTR)NULL;
  690. OpenFileName.lCustData = 0L;
  691. OpenFileName.lpfnHook = lpDlgHook;
  692. g_nBoxCount++;
  693. switch (titleId)
  694. {
  695. case DLG_Open_Filebox_Title:
  696. _tcscat(OpenFileName.lpstrFile, defExt);
  697. // fall thru
  698. case DLG_Browse_Executable_Title:
  699. case DLG_Browse_CrashDump_Title:
  700. result = GetOpenFileName((LPOPENFILENAME)&OpenFileName) ;
  701. break ;
  702. case DLG_Browse_LogFile_Title:
  703. if (fileName)
  704. {
  705. _tcscpy(OpenFileName.lpstrFile, fileName);
  706. }
  707. else
  708. {
  709. *OpenFileName.lpstrFile = 0;
  710. }
  711. result = GetOpenFileName((LPOPENFILENAME)&OpenFileName) ;
  712. break;
  713. case DLG_Browse_DbugDll_Title:
  714. *(OpenFileName.lpstrFile) = _T('\0');
  715. result = GetOpenFileName((LPOPENFILENAME)&OpenFileName) ;
  716. break ;
  717. case DLG_Browse_Filebox_Title:
  718. _tsplitpath (files, (PTSTR)NULL, (PTSTR)NULL, (PTSTR)szBase, szExt);
  719. indx = matchExt (szExt, filters);
  720. if (indx != -1)
  721. {
  722. OpenFileName.nFilterIndex = indx;
  723. }
  724. _tcscat(title, szBase);
  725. if (*szExt)
  726. {
  727. _tcscat(title, szExt);
  728. }
  729. FAddToSearchPath = FALSE;
  730. FAddToRootMap = FALSE;
  731. result = GetOpenFileName((LPOPENFILENAME)&OpenFileName) ;
  732. //
  733. // Check to see if the use said to add a file to the browse path.
  734. // If so then add it to the front of the path
  735. //
  736. /*if (FAddToSearchPath)
  737. {
  738. AddToSearchPath(OpenFileName.lpstrFile);
  739. }
  740. else if (FAddToRootMap)
  741. {
  742. RootSetMapped(fileName, OpenFileName.lpstrFile);
  743. }*/
  744. break;
  745. default:
  746. Assert(FALSE);
  747. free(LocalInitialDir);
  748. return FALSE;
  749. }
  750. g_nBoxCount--;
  751. if (result)
  752. {
  753. _tcscpy(fileName, OpenFileName.lpstrFile);
  754. if (titleId == DLG_Open_Filebox_Title)
  755. {
  756. AddFileToMru(FILE_USE_SOURCE, fileName);
  757. }
  758. //
  759. // Get the output of flags
  760. //
  761. *pFlags = OpenFileName.Flags ;
  762. }
  763. //
  764. //Restore cursor
  765. //
  766. SetCursor(hSaveCursor);
  767. free(LocalInitialDir);
  768. return result;
  769. } /* StartFileDlg() */
  770. /*** matchExt
  771. **
  772. ** Synopsis:
  773. ** int = matchExt (queryExtension, sourceList)
  774. **
  775. ** Entry:
  776. **
  777. ** Returns: 1-based index of pairwise substring for which the second
  778. ** element (i.e., the extension list), contains the target
  779. ** extension. If there is no match, we return -1.
  780. **
  781. ** Description:
  782. ** Searches extension lists for the Open/Save/Browse common
  783. ** dialogs to try to match a filter to the input filename's
  784. ** extension.
  785. ** (Open File, Save File, Merge File and Open Project)
  786. **
  787. ** Implementation note: Our thinking looks like this:
  788. **
  789. ** We are given a sequence of null-terminated strings which
  790. ** are text/extension pairs. We return the pairwise 1-based
  791. ** index of the first pair for which the second element has an
  792. ** exact match for the target extension. (Everything, by the
  793. ** way, is compared without case sensitivity.) We picture the
  794. ** source sequence, then, to be an array whose elements are pairs
  795. ** of strings (we will call the pairs 'left' and 'right').
  796. **
  797. ** Just to complicate things, we allow the '.right' pair elements to
  798. ** be strings like "*.c;*.cpp;*.cxx", where we our query might be
  799. ** any one of the three (minus the leading asterisk). Fortunately,
  800. ** _tcstok() will break things apart for us (see the 'delims[]' array
  801. ** in the code for the delimiters we have chosen).
  802. **
  803. ** Assuming there is a match in there somewhere, our invariant
  804. ** for locating the first one will be:
  805. **
  806. ** Exists(k):
  807. ** ForAll(i) : 0 <= i < k
  808. ** : queryExtension \not IS_IN source[i].right
  809. ** \and
  810. ** queryExtension IS_IN source[k].right
  811. **
  812. ** where we define IS_IN to be a membership predicate (using _tcstok()
  813. ** and _tcsicmp() in the implementation, eh?):
  814. **
  815. ** x IS_IN y
  816. ** <=>
  817. ** Exists (t:token) : (t \in y) \and (x == t).
  818. **
  819. ** The guard for our main loop, then, comes from the search for the
  820. ** queryExtension within the tokens inside successive '.right' elements.
  821. ** We choose to continue as long as there is no current token in the
  822. ** pair's right side that contains the query.
  823. **
  824. ** (We have the pragmatic concern that the value may not be there, so we
  825. ** augment the loop guard with the condition that we have not yet
  826. ** exhausted the source. This is straightforward to add to the
  827. ** invariant, but it causes a lot of clutter that does help our
  828. ** comprehension at all, so we just stick it in the guard without
  829. ** formal justification.)
  830. */
  831. int
  832. matchExt(
  833. PTSTR queryExtension,
  834. PTSTR sourceList
  835. )
  836. {
  837. int answer;
  838. int idxPair = 1; // a 1-based index!
  839. PTSTR tokenMatch = 0;
  840. TCHAR delims[] = _T("*,; ") ; // Given a typical string: "*.c;*.cpp;*.cxx",
  841. // _tcstok() would produce three tokens:
  842. // ".c", ".cpp", and ".cxx".
  843. while (*sourceList != 0 && tokenMatch == 0)
  844. {
  845. while (*sourceList != _T('\0'))
  846. { sourceList++; } // skip first string of pair
  847. sourceList++; // and increment beyond NULL
  848. if (*sourceList != _T('\0'))
  849. {
  850. PTSTR work = _tcsdup (sourceList); // copy to poke holes in
  851. tokenMatch = _tcstok (work, delims);
  852. while (tokenMatch && _tcsicmp (tokenMatch, queryExtension))
  853. {
  854. tokenMatch = _tcstok (0, delims);
  855. }
  856. free (work);
  857. }
  858. if (tokenMatch == 0) // no match: need to move to next pair
  859. {
  860. while (*sourceList != _T('\0'))
  861. { sourceList++; } // skip second string of pair
  862. sourceList++; // and increment beyond NULL
  863. idxPair++;
  864. }
  865. }
  866. answer = (tokenMatch != 0) ? idxPair : (-1);
  867. return (answer);
  868. }
  869. /*** DlgFile
  870. **
  871. ** Synopsis:
  872. ** bool = DlgFile(hDlg, message, wParam, lParam)
  873. **
  874. ** Entry:
  875. **
  876. ** Returns:
  877. **
  878. ** Description:
  879. ** Processes messages for file dialog boxes
  880. ** Those dialogs are not called directly but are called
  881. ** by the DlgFile function which contains all basic
  882. ** elements for Dialogs Files Operations Handling.
  883. ** (Open File, Save File, Merge File and Open Project)
  884. **
  885. ** See OFNHookProc
  886. */
  887. UINT_PTR
  888. APIENTRY
  889. DlgFile(
  890. HWND hDlg,
  891. UINT uMsg,
  892. WPARAM wParam,
  893. LPARAM lParam
  894. )
  895. {
  896. switch (uMsg)
  897. {
  898. case WM_NOTIFY:
  899. {
  900. LPOFNOTIFY lpon = (LPOFNOTIFY) lParam;
  901. //
  902. // Determine what happened/why we are being notified
  903. //
  904. switch (lpon->hdr.code)
  905. {
  906. case CDN_HELP:
  907. // Help button pushed
  908. Dbg(HtmlHelp(hDlg,g_HelpFileName, HH_HELP_CONTEXT,
  909. g_CurHelpId));
  910. break;
  911. }
  912. }
  913. break;
  914. }
  915. return FALSE;
  916. } /* DlgFile() */
  917. UINT_PTR
  918. APIENTRY
  919. GetOpenFileNameHookProc(
  920. HWND hDlg,
  921. UINT msg,
  922. WPARAM wParam,
  923. LPARAM lParam
  924. )
  925. /*++
  926. Routine Description:
  927. This routine is handle the Add Directory To radio buttons in the
  928. browser source file dialog box.
  929. Arguments:
  930. hDlg - Supplies the handle to current dialog
  931. msg - Supplies the message to be processed
  932. wParam - Supplies info about the message
  933. lParam - Supplies info about the message
  934. Return Value:
  935. TRUE if we replaced default processing of the message, FALSE otherwise
  936. --*/
  937. {
  938. /*
  939. switch( msg ) {
  940. case WM_INITDIALOG:
  941. return TRUE;
  942. case WM_NOTIFY:
  943. {
  944. LPOFNOTIFY lpon = (LPOFNOTIFY) lParam;
  945. switch(lpon->hdr.code) {
  946. case CDN_FILEOK:
  947. FAddToSearchPath = (IsDlgButtonChecked( hDlg, IDC_CHECK_ADD_SRC_ROOT_MAPPING) == BST_CHECKED);
  948. return 0;
  949. }
  950. }
  951. }
  952. return DlgFile(hDlg, msg, wParam, lParam);
  953. */
  954. return 0;
  955. } /* GetOpenFileNameHookProc() */
  956. void
  957. Internal_Activate(
  958. HWND hwndCur,
  959. HWND hwndNew,
  960. int nPosition
  961. )
  962. /*++
  963. Routine Description:
  964. Places a window in the specified Z order position.
  965. Arguments:
  966. hwndCur - Currently active window, topmost in Z order. Can be NULL.
  967. hwndNew - The window to be placed in the new Z order.
  968. nPosition - Where the window is to be place in the Z order.
  969. 1 - topmost
  970. 2 - 2nd place (behind topmost)
  971. 3 - 3rd place, etc....
  972. Return Value:
  973. None
  974. --*/
  975. {
  976. // Sanity check. Make sure the programmer
  977. // specified a 1, 2, or 3. We are strict in order to
  978. // keep it readable.
  979. Assert(1 <= nPosition && nPosition <= 3);
  980. Assert(hwndNew);
  981. switch (nPosition) {
  982. case 1:
  983. // Make it topmost
  984. SendMessage(g_hwndMDIClient, WM_MDIACTIVATE, (WPARAM) hwndNew, 0);
  985. break;
  986. case 2:
  987. // Try to place it 2nd in Z order
  988. if (NULL == hwndCur) {
  989. // We don't have a topmost window, so make this one the topmost window
  990. SendMessage(g_hwndMDIClient, WM_MDIACTIVATE, (WPARAM) hwndNew, 0);
  991. } else {
  992. // Place it in 2nd
  993. SetWindowPos(hwndNew, hwndCur, 0, 0, 0, 0,
  994. SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
  995. // Give the topmost most focus again and activate UI visual clues.
  996. SendMessage(g_hwndMDIClient, WM_MDIACTIVATE, (WPARAM) hwndCur, 0);
  997. }
  998. break;
  999. case 3:
  1000. // Try to place it 3rd in Z order
  1001. if (NULL == hwndCur) {
  1002. // We don't have a topmost window, so make this one the topmost window
  1003. SendMessage(g_hwndMDIClient, WM_MDIACTIVATE, (WPARAM) hwndNew, 0);
  1004. } else {
  1005. // Is there a window 2nd in the Z order?
  1006. HWND hwndPlaceAfter = GetNextWindow(hwndCur, GW_HWNDNEXT);
  1007. if (NULL == hwndPlaceAfter) {
  1008. // No window 2nd in Z order. Then simply place it after the
  1009. // topmost window.
  1010. hwndPlaceAfter = hwndCur;
  1011. }
  1012. // Place it
  1013. SetWindowPos(hwndNew, hwndPlaceAfter, 0, 0, 0, 0,
  1014. SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
  1015. // Give the topmost most focus again and activate UI visual clues.
  1016. SendMessage(g_hwndMDIClient, WM_MDIACTIVATE, (WPARAM) hwndCur, 0);
  1017. }
  1018. break;
  1019. default:
  1020. // Sanity check, the programmer missed a case
  1021. Assert(0);
  1022. }
  1023. }
  1024. void
  1025. ActivateMDIChild(
  1026. HWND hwndNew,
  1027. BOOL bUserActivated
  1028. )
  1029. /*++
  1030. Routine Description:
  1031. Used to activate a specified window. Automatically uses the hwndActive
  1032. variable to determine the currently active window.
  1033. Arguments:
  1034. hwndNew - The window to be placed in the new Z order.
  1035. bUserActivated - Indicates whether this action was initiated by the
  1036. user or by windbg. The value is to determine the Z order of
  1037. any windows that are opened.
  1038. --*/
  1039. {
  1040. if (hwndNew == NULL)
  1041. {
  1042. Assert(hwndNew);
  1043. return;
  1044. }
  1045. HWND hwndPrev = NULL;
  1046. PCOMMONWIN_DATA pCur_WinData = NULL;
  1047. PCOMMONWIN_DATA pNew_WinData = NULL;
  1048. PCOMMONWIN_DATA pPrev_WinData = NULL;
  1049. HWND hwndCur = MDIGetActive(g_hwndMDIClient, NULL);
  1050. if (!hwndCur || bUserActivated || hwndCur == hwndNew)
  1051. {
  1052. // Nothing else was open. So we make this one the
  1053. // topmost window.
  1054. //
  1055. // Or the user requested that this window be made
  1056. // the topmost window, and we obey.
  1057. //
  1058. // Or we are re-activating the current window.
  1059. Internal_Activate(hwndCur, hwndNew, 1);
  1060. return;
  1061. }
  1062. // See is we have 3 or more windows open
  1063. hwndPrev = GetNextWindow(hwndCur, GW_HWNDNEXT);
  1064. if (hwndCur)
  1065. {
  1066. pCur_WinData = GetCommonWinData(hwndCur);
  1067. }
  1068. pNew_WinData = GetCommonWinData(hwndNew);
  1069. Assert(pNew_WinData);
  1070. if (!pNew_WinData)
  1071. {
  1072. return;
  1073. }
  1074. if (hwndPrev)
  1075. {
  1076. pPrev_WinData = GetCommonWinData(hwndPrev);
  1077. }
  1078. //
  1079. // Handle the case where the window activation
  1080. // was requested by the debugger itself and not the
  1081. // user.
  1082. //
  1083. switch (pNew_WinData->m_enumType)
  1084. {
  1085. default:
  1086. Internal_Activate(hwndCur, hwndNew, bUserActivated ? 2 : 1);
  1087. break;
  1088. case DISASM_WINDOW:
  1089. case DOC_WINDOW:
  1090. if (GetSrcMode_StatusBar())
  1091. {
  1092. // Src mode
  1093. if (pCur_WinData != NULL &&
  1094. (DISASM_WINDOW == pCur_WinData->m_enumType ||
  1095. DOC_WINDOW == pCur_WinData->m_enumType))
  1096. {
  1097. // We can take the place of another doc/asm wind
  1098. // Place 1st in z-order
  1099. Internal_Activate(hwndCur, hwndNew, 1);
  1100. }
  1101. else
  1102. {
  1103. if (pPrev_WinData != NULL &&
  1104. (DOC_WINDOW == pPrev_WinData->m_enumType ||
  1105. DISASM_WINDOW == pPrev_WinData->m_enumType))
  1106. {
  1107. // Don't have a window in 2nd place, or if we do it
  1108. // is a src or asm window, and we can hide it.
  1109. // Place 2nd in Z-order
  1110. Internal_Activate(hwndCur, hwndNew, 2);
  1111. }
  1112. else
  1113. {
  1114. // Place 3rd in Z-order
  1115. Internal_Activate(hwndCur, hwndNew, 3);
  1116. }
  1117. }
  1118. }
  1119. else
  1120. {
  1121. WIN_TYPES Type = pCur_WinData != NULL ?
  1122. pCur_WinData->m_enumType : MINVAL_WINDOW;
  1123. // Asm mode
  1124. // Which is currently the topmost window.
  1125. switch (Type)
  1126. {
  1127. case DOC_WINDOW:
  1128. // Place 1st in z-order
  1129. Internal_Activate(hwndCur, hwndNew, 1);
  1130. break;
  1131. case DISASM_WINDOW:
  1132. if (DOC_WINDOW == pNew_WinData->m_enumType)
  1133. {
  1134. if (pPrev_WinData == NULL ||
  1135. DOC_WINDOW != pPrev_WinData->m_enumType)
  1136. {
  1137. // We have a window in second place that isn't a doc
  1138. // window (locals, watch, ...).
  1139. Internal_Activate(hwndCur, hwndNew, 3);
  1140. }
  1141. else
  1142. {
  1143. // Either don't have any windows in second place, or
  1144. // we have a window in second place that is a doc
  1145. // window. We can take its place.
  1146. //
  1147. // Place 2nd in z-order
  1148. Internal_Activate(hwndCur, hwndNew, 2);
  1149. }
  1150. }
  1151. else
  1152. {
  1153. // Should never happen. The case of disasm being activated
  1154. // when it is currently active should ahve already been
  1155. // taken care of.
  1156. Dbg(0);
  1157. }
  1158. break;
  1159. default:
  1160. if ((pPrev_WinData != NULL &&
  1161. DISASM_WINDOW == pPrev_WinData->m_enumType) &&
  1162. DOC_WINDOW == pNew_WinData->m_enumType)
  1163. {
  1164. // window (locals, watch, ...).
  1165. Internal_Activate(hwndCur, hwndNew, 3);
  1166. }
  1167. else
  1168. {
  1169. // Place 2nd in z-order
  1170. Internal_Activate(hwndCur, hwndNew, 2);
  1171. }
  1172. break;
  1173. }
  1174. }
  1175. break;
  1176. }
  1177. }
  1178. void
  1179. AppendTextToAnEditControl(
  1180. HWND hwnd,
  1181. PTSTR pszNewText
  1182. )
  1183. {
  1184. Assert(hwnd);
  1185. Assert(pszNewText);
  1186. CHARRANGE chrrgCurrent = {0};
  1187. CHARRANGE chrrgAppend = {0};
  1188. // Get the current selection
  1189. SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM) &chrrgCurrent);
  1190. // Set the selection to the very end of the edit control
  1191. chrrgAppend.cpMin = chrrgAppend.cpMax = GetWindowTextLength(hwnd);
  1192. SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &chrrgCurrent);
  1193. // Append the text
  1194. SendMessage(hwnd, EM_REPLACESEL, FALSE, (LPARAM) pszNewText);
  1195. // Restore previous selection
  1196. SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &chrrgCurrent);
  1197. }
  1198. VOID
  1199. CopyToClipboard(
  1200. PSTR str
  1201. )
  1202. {
  1203. if (!str)
  1204. {
  1205. return;
  1206. }
  1207. ULONG Len = strlen(str)+1;
  1208. HANDLE Mem = GlobalAlloc(GMEM_MOVEABLE, Len);
  1209. if (Mem == NULL)
  1210. {
  1211. return;
  1212. }
  1213. PSTR Text = (PSTR)GlobalLock(Mem);
  1214. if (Text == NULL)
  1215. {
  1216. GlobalFree(Mem);
  1217. return;
  1218. }
  1219. strcpy(Text, str);
  1220. GlobalUnlock(Mem);
  1221. if (OpenClipboard(NULL))
  1222. {
  1223. EmptyClipboard();
  1224. if (SetClipboardData(CF_TEXT, Mem) == NULL)
  1225. {
  1226. GlobalFree(Mem);
  1227. }
  1228. CloseClipboard();
  1229. }
  1230. }
  1231. void
  1232. SetAllocString(PSTR* Str, PSTR New)
  1233. {
  1234. if (*Str != NULL)
  1235. {
  1236. free(*Str);
  1237. }
  1238. *Str = New;
  1239. }
  1240. BOOL
  1241. DupAllocString(PSTR* Str, PSTR New)
  1242. {
  1243. PSTR NewStr = (PSTR)malloc(strlen(New) + 1);
  1244. if (NewStr == NULL)
  1245. {
  1246. return FALSE;
  1247. }
  1248. strcpy(NewStr, New);
  1249. SetAllocString(Str, NewStr);
  1250. return TRUE;
  1251. }
  1252. BOOL
  1253. PrintAllocString(PSTR* Str, int Len, PCSTR Format, ...)
  1254. {
  1255. PSTR NewStr = (PSTR)malloc(Len);
  1256. if (NewStr == NULL)
  1257. {
  1258. return FALSE;
  1259. }
  1260. va_list Args;
  1261. va_start(Args, Format);
  1262. _vsnprintf(NewStr, Len, Format, Args);
  1263. va_end(Args);
  1264. SetAllocString(Str, NewStr);
  1265. return TRUE;
  1266. }
  1267. HMENU
  1268. CreateContextMenuFromToolbarButtons(ULONG NumButtons,
  1269. TBBUTTON* Buttons,
  1270. ULONG IdBias)
  1271. {
  1272. ULONG i;
  1273. HMENU Menu;
  1274. Menu = CreatePopupMenu();
  1275. if (Menu == NULL)
  1276. {
  1277. return Menu;
  1278. }
  1279. for (i = 0; i < NumButtons; i++)
  1280. {
  1281. MENUITEMINFO Item;
  1282. ZeroMemory(&Item, sizeof(Item));
  1283. Item.cbSize = sizeof(Item);
  1284. Item.fMask = MIIM_TYPE;
  1285. if (Buttons->fsStyle & BTNS_SEP)
  1286. {
  1287. Item.fType = MFT_SEPARATOR;
  1288. }
  1289. else
  1290. {
  1291. Item.fMask |= MIIM_ID;
  1292. Item.fType = MFT_STRING;
  1293. Item.wID = (WORD)(Buttons->idCommand + IdBias);
  1294. Item.dwTypeData = (LPSTR)Buttons->iString;
  1295. }
  1296. if (!InsertMenuItem(Menu, i, TRUE, &Item))
  1297. {
  1298. DestroyMenu(Menu);
  1299. return NULL;
  1300. }
  1301. Buttons++;
  1302. }
  1303. DrawMenuBar(g_hwndFrame);
  1304. return Menu;
  1305. }
  1306. HWND
  1307. AddButtonBand(HWND Bar, PTSTR Text, PTSTR SizingText, UINT Id)
  1308. {
  1309. HWND Button;
  1310. HDC Dc;
  1311. RECT Rect;
  1312. Button = CreateWindowEx(0, "BUTTON", Text, WS_VISIBLE | WS_CHILD,
  1313. 0, 0, 0, 0,
  1314. Bar, (HMENU)(UINT_PTR)Id, g_hInst, NULL);
  1315. if (Button == NULL)
  1316. {
  1317. return NULL;
  1318. }
  1319. Rect.left = 0;
  1320. Rect.top = 0;
  1321. SendMessage(Button, WM_SETFONT, (WPARAM)g_Fonts[FONT_VARIABLE].Font, 0);
  1322. Dc = GetDC(Button);
  1323. if (Dc != NULL)
  1324. {
  1325. SIZE Size;
  1326. GetTextExtentPoint32(Dc, SizingText, strlen(SizingText), &Size);
  1327. Rect.right = Size.cx;
  1328. Rect.bottom = Size.cy;
  1329. ReleaseDC(Button, Dc);
  1330. }
  1331. else
  1332. {
  1333. Rect.right = strlen(Text) * g_Fonts[FONT_FIXED].Metrics.tmAveCharWidth;
  1334. Rect.bottom = g_Fonts[FONT_FIXED].Metrics.tmHeight;
  1335. }
  1336. REBARBANDINFO BandInfo;
  1337. BandInfo.cbSize = sizeof(BandInfo);
  1338. BandInfo.fMask = RBBIM_STYLE | RBBIM_CHILD | RBBIM_CHILDSIZE;
  1339. BandInfo.fStyle = RBBS_FIXEDSIZE;
  1340. BandInfo.hwndChild = Button;
  1341. BandInfo.cxMinChild = Rect.right - Rect.left +
  1342. 4 * GetSystemMetrics(SM_CXEDGE);
  1343. BandInfo.cyMinChild = Rect.bottom - Rect.top +
  1344. 2 * GetSystemMetrics(SM_CYEDGE);
  1345. SendMessage(Bar, RB_INSERTBAND, -1, (LPARAM)&BandInfo);
  1346. return Button;
  1347. }