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.

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