Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

858 lines
19 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. ntui.c
  5. Abstract:
  6. Processing indicator dialog shows percentage of completion
  7. in a progress bar. The progress is updated by the caller.
  8. Author:
  9. Jim Schmidt (jimschm) 13-Aug-1996
  10. Revision History:
  11. jimschm 19-Oct-1998 Updated to use wizard status line
  12. jimschm 23-Sep-1998 Redesigned domain account resolution
  13. jimschm 02-Jul-1998 Finally rewrote progress bar
  14. jimschm 18-Dec-1996 Moved to new lib, slated to be rewritten
  15. --*/
  16. #include "pch.h"
  17. #include "ntuip.h"
  18. static PCTSTR g_LocalAccountString;
  19. static PCTSTR g_SearchAgainString;
  20. static HWND g_StatusPopup;
  21. static OUR_CRITICAL_SECTION g_StatusPopupCs;
  22. static HANDLE g_AbortDelayEvent;
  23. static HANDLE g_DelayThread;
  24. static BOOL g_ClassRegistered = FALSE;
  25. static DWORD g_ThreadId;
  26. #define WMX_SETTEXT (WM_USER+500)
  27. #define S_STATUS_CLASS TEXT("StatusWnd")
  28. typedef struct {
  29. INT ConversionX;
  30. INT ConversionY;
  31. } CONVERSIONFACTORS, *PCONVERSIONFACTORS;
  32. #define CONVERSION_RESOLUTION 100
  33. VOID
  34. pShowStatusPopup (
  35. VOID
  36. );
  37. VOID
  38. pKillDelayThread (
  39. VOID
  40. );
  41. VOID
  42. pUpdateDialog (
  43. HWND hdlg,
  44. PRESOLVE_ACCOUNTS_ARRAY Array,
  45. BOOL UserList,
  46. BOOL DomainList
  47. )
  48. {
  49. HWND hwndUsers;
  50. HWND hwndDomain;
  51. UINT Count;
  52. TCHAR Buf[256];
  53. UINT Selection;
  54. UINT Index;
  55. UINT Item;
  56. PCTSTR *DomainNamePtr;
  57. PCTSTR Message;
  58. PCTSTR ArgArray[1];
  59. hwndUsers = GetDlgItem (hdlg, IDC_USER_LIST);
  60. hwndDomain = GetDlgItem (hdlg, IDC_DOMAIN_LIST);
  61. if (UserList) {
  62. //
  63. // Populate the list box with <user> logs onto <domain>
  64. //
  65. Selection = SendMessage (hwndUsers, LB_GETCURSEL, 0, 0);
  66. if (Selection == LB_ERR) {
  67. Selection = 0;
  68. }
  69. SendMessage (hwndUsers, LB_RESETCONTENT, 0, 0);
  70. for (Count = 0 ; Array[Count].UserName ; Count++) {
  71. if (Array[Count].RetryFlag) {
  72. wsprintf (
  73. Buf, TEXT("%s\t%s"),
  74. Array[Count].UserName,
  75. g_SearchAgainString
  76. );
  77. } else {
  78. wsprintf (
  79. Buf, TEXT("%s\t%s"),
  80. Array[Count].UserName,
  81. Array[Count].OutboundDomain ? Array[Count].OutboundDomain : g_LocalAccountString
  82. );
  83. }
  84. Item = SendMessage (hwndUsers, LB_ADDSTRING, 0, (LPARAM) Buf);
  85. SendMessage (hwndUsers, LB_SETITEMDATA, Item, Count);
  86. }
  87. SendMessage (hwndUsers, LB_SETCURSEL, Selection, 0);
  88. }
  89. if (DomainList) {
  90. //
  91. // Get the current user selection
  92. //
  93. Selection = SendMessage (hwndUsers, LB_GETCURSEL, 0, 0);
  94. if (Selection == LB_ERR) {
  95. Selection = 0;
  96. }
  97. Index = SendMessage (hwndUsers, LB_GETITEMDATA, Selection, 0);
  98. //
  99. // Fill the combo box
  100. //
  101. SendMessage (hwndDomain, CB_RESETCONTENT, 0, 0);
  102. DomainNamePtr = Array[Index].DomainArray;
  103. // Insert all domain names
  104. while (*DomainNamePtr) {
  105. Item = SendMessage (hwndDomain, CB_ADDSTRING, 0, (LPARAM) (*DomainNamePtr));
  106. SendMessage (hwndDomain, CB_SETITEMDATA, Item, (LPARAM) (*DomainNamePtr));
  107. DomainNamePtr++;
  108. }
  109. // Insert standard strings
  110. Item = SendMessage (hwndDomain, CB_ADDSTRING, 0, (LPARAM) g_LocalAccountString);
  111. SendMessage (hwndDomain, CB_SETITEMDATA, Item, (LPARAM) g_LocalAccountString);
  112. Item = SendMessage (hwndDomain, CB_ADDSTRING, 0, (LPARAM) g_SearchAgainString);
  113. SendMessage (hwndDomain, CB_SETITEMDATA, Item, (LPARAM) g_SearchAgainString);
  114. // Restore selection
  115. if (Array[Index].RetryFlag) {
  116. Item = SendMessage (hwndDomain, CB_FINDSTRINGEXACT, 0, (LPARAM) g_SearchAgainString);
  117. SendMessage (hwndDomain, CB_SETCURSEL, Item, 0);
  118. } else if (Array[Index].OutboundDomain) {
  119. Item = SendMessage (hwndDomain, CB_FINDSTRINGEXACT, 0, (LPARAM) (Array[Index].OutboundDomain));
  120. SendMessage (hwndDomain, CB_SETCURSEL, Item, 0);
  121. } else {
  122. Item = SendMessage (hwndDomain, CB_FINDSTRINGEXACT, 0, (LPARAM) g_LocalAccountString);
  123. SendMessage (hwndDomain, CB_SETCURSEL, Item, 0);
  124. }
  125. ArgArray[0] = Array[Index].UserName;
  126. Message = ParseMessageID (MSG_USER_DOMAIN_LOGON_DLG, ArgArray);
  127. SetDlgItemText (hdlg, IDC_DOMAIN_LIST_TITLE, Message);
  128. FreeStringResource (Message);
  129. }
  130. }
  131. VOID
  132. pInitConversionFactors (
  133. IN HWND hdlg,
  134. OUT PCONVERSIONFACTORS Factors
  135. )
  136. {
  137. RECT rect;
  138. rect.left = 0;
  139. rect.right = CONVERSION_RESOLUTION;
  140. rect.top = 0;
  141. rect.bottom = CONVERSION_RESOLUTION;
  142. MapDialogRect (hdlg, &rect);
  143. Factors->ConversionX = rect.right - rect.left;
  144. Factors->ConversionY = rect.bottom - rect.top;
  145. }
  146. INT
  147. pConvertPixelsToDialogX (
  148. IN PCONVERSIONFACTORS Factors,
  149. IN INT Pixels
  150. )
  151. {
  152. return CONVERSION_RESOLUTION * Pixels / Factors->ConversionX;
  153. }
  154. INT
  155. pConvertPixelsToDialogY (
  156. IN PCONVERSIONFACTORS Factors,
  157. IN INT Pixels
  158. )
  159. {
  160. return CONVERSION_RESOLUTION * Pixels / Factors->ConversionY;
  161. }
  162. BOOL
  163. CALLBACK
  164. pResolveAccountsDlgProc (
  165. HWND hdlg,
  166. UINT uMsg,
  167. WPARAM wParam,
  168. LPARAM lParam
  169. )
  170. /*++
  171. Routine Description:
  172. pResolveAccountsDlgProc prompts the user with a list of domain choices,
  173. the local machine, or retry the network search.
  174. Upon init, the lParam specifies the RESULT_ACCOUNTS_ARRAY pointer that
  175. provides the user list and initial state. Upon exit, the array is
  176. updated to reflect the user's choices.
  177. Arguments:
  178. hdlg - The dialog handle
  179. uMsg - The message to process
  180. wParam - The wParam for the message
  181. lParam - The lParam for the message
  182. Return value:
  183. The dialog always ends with IDOK.
  184. --*/
  185. {
  186. static PRESOLVE_ACCOUNTS_ARRAY Array;
  187. static CONVERSIONFACTORS Factors;
  188. RECT rect;
  189. INT Tabs;
  190. UINT Selection;
  191. UINT Index;
  192. HWND hwndList;
  193. PCTSTR NewDomain;
  194. switch (uMsg) {
  195. case WM_INITDIALOG:
  196. CenterWindow (hdlg, GetDesktopWindow());
  197. Array = (PRESOLVE_ACCOUNTS_ARRAY) lParam;
  198. MYASSERT (Array);
  199. pInitConversionFactors (hdlg, &Factors);
  200. //
  201. // Get the strings
  202. //
  203. g_LocalAccountString = GetStringResource (MSG_LOCAL_ACCOUNT_DLG);
  204. g_SearchAgainString = GetStringResource (MSG_DOMAIN_NOT_LISTED_DLG);
  205. //
  206. // Set the tab stops
  207. //
  208. GetWindowRect (GetDlgItem (hdlg, IDC_USER_TITLE), &rect);
  209. Tabs = pConvertPixelsToDialogX (&Factors, (rect.right - rect.left) + 8);
  210. SendMessage (GetDlgItem (hdlg, IDC_USER_LIST), LB_SETTABSTOPS, 1, (LPARAM) &Tabs);
  211. //
  212. // Clear the retry flag
  213. //
  214. for (Index = 0 ; Array[Index].UserName ; Index++) {
  215. Array[Index].RetryFlag = FALSE;
  216. }
  217. //
  218. // Fill the controls
  219. //
  220. pUpdateDialog (hdlg, Array, TRUE, TRUE);
  221. return TRUE;
  222. case WM_COMMAND:
  223. switch (LOWORD (wParam)) {
  224. case IDOK:
  225. FreeStringResource (g_LocalAccountString);
  226. g_LocalAccountString = NULL;
  227. FreeStringResource (g_SearchAgainString);
  228. g_SearchAgainString = NULL;
  229. EndDialog (hdlg, IDOK);
  230. return TRUE;
  231. case IDC_USER_LIST:
  232. if (HIWORD (wParam) == LBN_SELCHANGE) {
  233. pUpdateDialog (hdlg, Array, FALSE, TRUE);
  234. }
  235. return TRUE;
  236. case IDC_DOMAIN_LIST:
  237. if (HIWORD (wParam) == CBN_SELCHANGE) {
  238. hwndList = GetDlgItem (hdlg, IDC_USER_LIST);
  239. Selection = SendMessage (hwndList, LB_GETCURSEL, 0, 0);
  240. Index = SendMessage (hwndList, LB_GETITEMDATA, Selection, 0);
  241. hwndList = GetDlgItem (hdlg, IDC_DOMAIN_LIST);
  242. Selection = SendMessage (hwndList, CB_GETCURSEL, 0, 0);
  243. NewDomain = (PCTSTR) SendMessage (hwndList, CB_GETITEMDATA, Selection, 0);
  244. if (NewDomain == g_LocalAccountString) {
  245. Array[Index].OutboundDomain = NULL;
  246. Array[Index].RetryFlag = FALSE;
  247. } else if (NewDomain == g_SearchAgainString) {
  248. Array[Index].OutboundDomain = NULL;
  249. Array[Index].RetryFlag = TRUE;
  250. } else {
  251. Array[Index].OutboundDomain = NewDomain;
  252. Array[Index].RetryFlag = FALSE;
  253. }
  254. pUpdateDialog (hdlg, Array, TRUE, FALSE);
  255. }
  256. }
  257. break;
  258. }
  259. return FALSE;
  260. }
  261. BOOL
  262. CALLBACK
  263. NetworkDownDlgProc (
  264. HWND hdlg,
  265. UINT uMsg,
  266. WPARAM wParam,
  267. LPARAM lParam
  268. )
  269. /*++
  270. Routine Description:
  271. NetworkDownDlgProc asks the user if they want to:
  272. (A) Continue searching with retry prompts
  273. (B) Continue searching, skipping down domains
  274. (C) Stop searching
  275. Arguments:
  276. hdlg - The dialog handle
  277. uMsg - The message to process
  278. wParam - The wParam for the message
  279. lParam - The lParam for the message
  280. Return value:
  281. The call to DialogBox returns:
  282. IDC_STOP - Stop searching
  283. IDC_RETRY - Continue with retry
  284. IDC_NO_RETRY - Continue without retry
  285. --*/
  286. {
  287. switch (uMsg) {
  288. case WM_INITDIALOG:
  289. CenterWindow (hdlg, GetDesktopWindow());
  290. CheckDlgButton (hdlg, IDC_RETRY, TRUE);
  291. return TRUE;
  292. case WM_COMMAND:
  293. switch (LOWORD (wParam)) {
  294. case IDOK:
  295. if (IsDlgButtonChecked (hdlg, IDC_RETRY)) {
  296. EndDialog (hdlg, IDC_RETRY);
  297. } else if (IsDlgButtonChecked (hdlg, IDC_NO_RETRY)) {
  298. EndDialog (hdlg, IDC_NO_RETRY);
  299. } else if (IsDlgButtonChecked (hdlg, IDC_STOP)) {
  300. EndDialog (hdlg, IDC_STOP);
  301. }
  302. return TRUE;
  303. }
  304. break;
  305. }
  306. return FALSE;
  307. }
  308. VOID
  309. ResolveAccounts (
  310. PRESOLVE_ACCOUNTS_ARRAY Array
  311. )
  312. {
  313. DialogBoxParam (
  314. g_hInst,
  315. MAKEINTRESOURCE (IDD_CHOOSE_DOMAIN),
  316. g_ParentWnd,
  317. pResolveAccountsDlgProc,
  318. (LPARAM) Array
  319. );
  320. }
  321. LRESULT
  322. CALLBACK
  323. pStatusWndProc (
  324. HWND hwnd,
  325. UINT uMsg,
  326. WPARAM wParam,
  327. LPARAM lParam
  328. )
  329. {
  330. RECT Desktop;
  331. RECT Client;
  332. INT Top, Left;
  333. INT Width, Height;
  334. INT TextWidth, TextHeight;
  335. static HWND StatusText;
  336. PCTSTR InitialMsg;
  337. TEXTMETRIC tm;
  338. HDC hdc;
  339. switch (uMsg) {
  340. case WM_CREATE:
  341. g_StatusPopup = hwnd;
  342. InitialMsg = GetStringResource (MSG_INITIAL_STATUS_MSG);
  343. MYASSERT (InitialMsg);
  344. //
  345. // Compute proper size
  346. //
  347. GetWindowRect (GetDesktopWindow(), &Desktop);
  348. hdc = CreateDC (TEXT("DISPLAY"), NULL, NULL, NULL);
  349. SelectObject (hdc, GetStockObject (DEFAULT_GUI_FONT));
  350. GetTextMetrics (hdc, &tm);
  351. DeleteDC (hdc);
  352. Width = (Desktop.right - Desktop.left) / 2;
  353. Height = (Desktop.bottom - Desktop.top) / 20;
  354. TextWidth = tm.tmAveCharWidth * 3 * TcharCount (InitialMsg);
  355. TextHeight = tm.tmHeight * 3;
  356. Width = min (Width, TextWidth);
  357. Height = min (Height, TextHeight);
  358. Top = Desktop.bottom - Height - tm.tmAveCharWidth;
  359. Left = Desktop.right - Width - tm.tmHeight;
  360. SetWindowPos (hwnd, HWND_TOPMOST, Left, Top, Width, Height, SWP_NOACTIVATE);
  361. //
  362. // Create text window
  363. //
  364. GetClientRect (hwnd, &Client);
  365. Width = (Client.right - Client.left) * 7 / 8;
  366. Height = (Client.bottom - Client.top) * 7 / 8;
  367. Top = (Client.right - Client.left) / 16;
  368. Left = (Client.bottom - Client.top) / 16;
  369. StatusText = CreateWindow (
  370. TEXT("STATIC"),
  371. InitialMsg,
  372. WS_CHILD|WS_VISIBLE|SS_NOPREFIX|SS_CENTERIMAGE,
  373. Top, Left,
  374. Width, Height,
  375. hwnd,
  376. (PVOID) 100,
  377. g_hInst,
  378. NULL
  379. );
  380. SendMessage (StatusText, WM_SETFONT, (WPARAM) GetStockObject (DEFAULT_GUI_FONT), 0);
  381. //
  382. // Make window initially hidden
  383. //
  384. HideStatusPopup (STATUS_DELAY);
  385. FreeStringResource (InitialMsg);
  386. return TRUE;
  387. case WMX_SETTEXT:
  388. SetWindowText (StatusText, (PCTSTR) lParam);
  389. break;
  390. case WM_DESTROY:
  391. if (StatusText) {
  392. DestroyWindow (StatusText);
  393. StatusText = NULL;
  394. }
  395. break;
  396. }
  397. return DefWindowProc (hwnd, uMsg, wParam, lParam);
  398. }
  399. DWORD
  400. WINAPI
  401. pStatusDlgThread (
  402. PVOID Arg
  403. )
  404. {
  405. WNDCLASS wc;
  406. HWND hwnd;
  407. MSG msg;
  408. if (!g_ClassRegistered) {
  409. ZeroMemory (&wc, sizeof (wc));
  410. wc.lpfnWndProc = pStatusWndProc;
  411. wc.hInstance = g_hInst;
  412. wc.hbrBackground = (HBRUSH) COLOR_WINDOW;
  413. wc.lpszClassName = S_STATUS_CLASS;
  414. RegisterClass (&wc);
  415. g_ClassRegistered = TRUE;
  416. }
  417. hwnd = CreateWindowEx (
  418. 0,
  419. S_STATUS_CLASS,
  420. TEXT(""),
  421. WS_POPUP|WS_BORDER|WS_THICKFRAME,
  422. CW_USEDEFAULT, CW_USEDEFAULT,
  423. CW_USEDEFAULT, CW_USEDEFAULT,
  424. g_ParentWnd,
  425. NULL,
  426. g_hInst,
  427. NULL
  428. );
  429. while (GetMessage (&msg, NULL, 0, 0)) {
  430. if (msg.hwnd == NULL) {
  431. if (msg.message == WM_CLOSE) {
  432. break;
  433. }
  434. }
  435. TranslateMessage (&msg);
  436. DispatchMessage (&msg);
  437. }
  438. DestroyWindow (g_StatusPopup);
  439. g_StatusPopup = NULL;
  440. return 0;
  441. }
  442. VOID
  443. CreateStatusPopup (
  444. VOID
  445. )
  446. {
  447. HWND Child;
  448. PCTSTR InitialMsg;
  449. g_StatusPopup = GetDlgItem (g_ParentWnd, IDC_PROGRESS_BAR_LABEL);
  450. if (!g_StatusPopup) {
  451. //
  452. // Scan all children for IDC_PROGRESS_BAR_LABEL
  453. //
  454. Child = GetWindow (g_ParentWnd, GW_CHILD);
  455. while (Child) {
  456. g_StatusPopup = GetDlgItem (Child, IDC_PROGRESS_BAR_LABEL);
  457. if (g_StatusPopup) {
  458. break;
  459. }
  460. Child = GetWindow (Child, GW_HWNDNEXT);
  461. }
  462. }
  463. MYASSERT (g_StatusPopup);
  464. HideStatusPopup (STATUS_DELAY);
  465. InitialMsg = GetStringResource (MSG_INITIAL_STATUS_MSG);
  466. if (InitialMsg) {
  467. SetWindowText (g_StatusPopup, InitialMsg);
  468. FreeStringResource (InitialMsg);
  469. }
  470. #if 0
  471. HANDLE Thread;
  472. InitializeOurCriticalSection (&g_StatusPopupCs);
  473. Thread = CreateThread (
  474. NULL,
  475. 0,
  476. pStatusDlgThread,
  477. NULL,
  478. 0,
  479. &g_ThreadId
  480. );
  481. MYASSERT (Thread);
  482. CloseHandle (Thread);
  483. #endif
  484. }
  485. VOID
  486. DestroyStatusPopup (
  487. VOID
  488. )
  489. {
  490. pKillDelayThread();
  491. EnterOurCriticalSection (&g_StatusPopupCs);
  492. if (g_StatusPopup) {
  493. ShowWindow (g_StatusPopup, SW_HIDE);
  494. //PostThreadMessage (g_ThreadId, WM_CLOSE, 0, 0);
  495. }
  496. if (g_AbortDelayEvent) {
  497. CloseHandle (g_AbortDelayEvent);
  498. g_AbortDelayEvent = NULL;
  499. }
  500. LeaveOurCriticalSection (&g_StatusPopupCs);
  501. DeleteOurCriticalSection (&g_StatusPopupCs);
  502. }
  503. VOID
  504. UpdateStatusPopup (
  505. PCTSTR NewMessage
  506. )
  507. {
  508. EnterOurCriticalSection (&g_StatusPopupCs);
  509. if (g_StatusPopup) {
  510. SetWindowText (g_StatusPopup, NewMessage);
  511. #if 0
  512. SendMessage (g_StatusPopup, WMX_SETTEXT, 0, (LPARAM) NewMessage);
  513. #endif
  514. }
  515. LeaveOurCriticalSection (&g_StatusPopupCs);
  516. }
  517. DWORD
  518. WINAPI
  519. pDelayThenShowStatus (
  520. PVOID Arg
  521. )
  522. {
  523. DWORD Result;
  524. Result = WaitForSingleObject (g_AbortDelayEvent, (UINT) Arg);
  525. if (WAIT_TIMEOUT == Result) {
  526. EnterOurCriticalSection (&g_StatusPopupCs);
  527. pShowStatusPopup();
  528. LeaveOurCriticalSection (&g_StatusPopupCs);
  529. }
  530. EnterOurCriticalSection (&g_StatusPopupCs);
  531. if (g_AbortDelayEvent) {
  532. CloseHandle (g_AbortDelayEvent);
  533. g_AbortDelayEvent = NULL;
  534. }
  535. LeaveOurCriticalSection (&g_StatusPopupCs);
  536. return 0;
  537. }
  538. VOID
  539. pKillDelayThread (
  540. VOID
  541. )
  542. {
  543. //
  544. // This routine makes sure the delay thread is stopped,
  545. // that the thread handle is closed, and that the show event
  546. // is cleaned up.
  547. //
  548. // There is no affect on the visibility of the status dialog.
  549. //
  550. if (!g_DelayThread) {
  551. return;
  552. }
  553. EnterOurCriticalSection (&g_StatusPopupCs);
  554. if (g_AbortDelayEvent) {
  555. SetEvent (g_AbortDelayEvent);
  556. }
  557. LeaveOurCriticalSection (&g_StatusPopupCs);
  558. WaitForSingleObject (g_DelayThread, INFINITE);
  559. EnterOurCriticalSection (&g_StatusPopupCs);
  560. CloseHandle (g_DelayThread);
  561. g_DelayThread = NULL;
  562. LeaveOurCriticalSection (&g_StatusPopupCs);
  563. }
  564. VOID
  565. HideStatusPopup (
  566. UINT Timeout
  567. )
  568. {
  569. pKillDelayThread();
  570. EnterOurCriticalSection (&g_StatusPopupCs);
  571. ShowWindow (g_StatusPopup, SW_HIDE);
  572. if (Timeout != INFINITE) {
  573. MYASSERT (!g_DelayThread);
  574. MYASSERT (!g_AbortDelayEvent);
  575. g_AbortDelayEvent = CreateEvent (NULL, TRUE, FALSE, NULL);
  576. g_DelayThread = StartThread (pDelayThenShowStatus, (PVOID) Timeout);
  577. }
  578. LeaveOurCriticalSection (&g_StatusPopupCs);
  579. }
  580. VOID
  581. pShowStatusPopup (
  582. VOID
  583. )
  584. {
  585. //
  586. // Caller handles mutex
  587. //
  588. if (g_StatusPopup) {
  589. ShowWindow (g_StatusPopup, SW_SHOW);
  590. UpdateWindow (g_StatusPopup);
  591. }
  592. #if 0
  593. if (g_StatusPopup) {
  594. SetWindowPos (
  595. g_StatusPopup,
  596. HWND_TOPMOST,
  597. 0, 0, 0, 0,
  598. SWP_NOMOVE|SWP_NOSIZE|SWP_SHOWWINDOW|SWP_NOACTIVATE
  599. );
  600. UpdateWindow (g_StatusPopup);
  601. }
  602. #endif
  603. }
  604. VOID
  605. ShowStatusPopup (
  606. VOID
  607. )
  608. {
  609. pKillDelayThread();
  610. EnterOurCriticalSection (&g_StatusPopupCs);
  611. pShowStatusPopup();
  612. LeaveOurCriticalSection (&g_StatusPopupCs);
  613. }
  614. BOOL
  615. IsStatusPopupVisible (
  616. VOID
  617. )
  618. {
  619. BOOL b;
  620. EnterOurCriticalSection (&g_StatusPopupCs);
  621. b = IsWindowVisible (g_StatusPopup);
  622. LeaveOurCriticalSection (&g_StatusPopupCs);
  623. return b;
  624. }