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.

802 lines
18 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. progbar.c
  5. Abstract:
  6. Centralizes access to the progress bar and associated messages accross components
  7. (hwcomp,migapp,etc.) and sides (w9x, nt)
  8. Author:
  9. Marc R. Whitten (marcw) 14-Apr-1997
  10. Revision History:
  11. jimschm 19-Jun-1998 Improved to allow revision of estimates, necessary
  12. for NT-side progress bar.
  13. --*/
  14. #include "pch.h"
  15. #include "migshared.h"
  16. //
  17. // Types
  18. //
  19. typedef struct {
  20. BOOL Started;
  21. BOOL Completed;
  22. UINT InitialGuess;
  23. UINT TotalTicks;
  24. UINT TicksSoFar;
  25. UINT LastTickDisplayed;
  26. } SLICE, *PSLICE;
  27. typedef struct {
  28. HWND Window;
  29. HANDLE CancelEvent;
  30. PCSTR Message;
  31. DWORD MessageId;
  32. DWORD Delay;
  33. BOOL InUse;
  34. } DELAYTHREADPARAMS, *PDELAYTHREADPARAMS;
  35. #if 0
  36. typedef struct {
  37. HANDLE CancelEvent;
  38. DWORD TickCount;
  39. BOOL InUse;
  40. } TICKTHREADPARAMS, *PTICKTHREADPARAMS;
  41. #endif
  42. //
  43. // Globals
  44. //
  45. BOOL g_ProgBarInitialized = FALSE;
  46. HWND g_ProgressBar;
  47. HWND g_Component;
  48. HWND g_SubComponent;
  49. PBRANGE g_OrgRange;
  50. HANDLE g_ComponentCancelEvent;
  51. HANDLE g_SubComponentCancelEvent;
  52. BOOL *g_CancelFlagPtr;
  53. GROWBUFFER g_SliceArray;
  54. UINT g_SliceCount;
  55. UINT g_MaxTickCount;
  56. UINT g_PaddingTicks;
  57. UINT g_CurrentTickCount;
  58. UINT g_CurrentPos;
  59. UINT g_ReduceFactor;
  60. BOOL g_Reverse = FALSE;
  61. OUR_CRITICAL_SECTION g_ProgBarCriticalSection;
  62. UINT g_CurrentSliceId = (UINT)-1;
  63. //
  64. // Tuning constants
  65. //
  66. #define TICKSCALE 100
  67. //
  68. // Implementaiton
  69. //
  70. VOID
  71. InitializeProgressBar (
  72. IN HWND ProgressBar,
  73. IN HWND Component, OPTIONAL
  74. IN HWND SubComponent, OPTIONAL
  75. IN BOOL *CancelFlagPtr OPTIONAL
  76. )
  77. {
  78. LONG rc;
  79. CHAR Data[256];
  80. DWORD Size;
  81. HKEY Key;
  82. g_ProgressBar = ProgressBar;
  83. g_CancelFlagPtr = CancelFlagPtr;
  84. g_ProgBarInitialized = TRUE;
  85. SendMessage (ProgressBar, PBM_SETPOS, 0, 0);
  86. g_CurrentPos = 0;
  87. SendMessage (ProgressBar, PBM_GETRANGE, 0, (LPARAM) &g_OrgRange);
  88. //
  89. // Create cancel events for delayed messages.
  90. //
  91. g_ComponentCancelEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
  92. g_SubComponentCancelEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
  93. if (!g_ComponentCancelEvent || !g_SubComponentCancelEvent) {
  94. DEBUGMSG ((DBG_ERROR, "ProgressBar: Could not create cancel events."));
  95. }
  96. InitializeOurCriticalSection (&g_ProgBarCriticalSection);
  97. g_Component = Component;
  98. g_SubComponent = SubComponent;
  99. DEBUGMSG_IF ((
  100. Component && !IsWindow (Component),
  101. DBG_WHOOPS,
  102. "Progress bar component is not a valid window"
  103. ));
  104. DEBUGMSG_IF ((
  105. SubComponent && !IsWindow (SubComponent),
  106. DBG_WHOOPS,
  107. "Progress bar sub component is not a valid window"
  108. ));
  109. MYASSERT (!g_SliceCount);
  110. MYASSERT (!g_SliceArray.Buf);
  111. MYASSERT (!g_MaxTickCount);
  112. MYASSERT (!g_PaddingTicks);
  113. MYASSERT (!g_CurrentTickCount);
  114. MYASSERT (g_CurrentSliceId == (UINT)-1);
  115. g_ReduceFactor = 1;
  116. Key = OpenRegKeyStrA ("HKLM\\inapoi");
  117. if (Key) {
  118. Size = 256;
  119. rc = RegQueryValueExA (Key, "", NULL, NULL, (PBYTE) Data, &Size);
  120. CloseRegKey (Key);
  121. #pragma prefast(suppress:400, "not concerned about different code page")
  122. if (rc == ERROR_SUCCESS && !lstrcmpiA (Data, "backwards")) {
  123. g_Reverse = TRUE;
  124. }
  125. }
  126. }
  127. VOID
  128. TerminateProgressBar (
  129. VOID
  130. )
  131. {
  132. if (g_ComponentCancelEvent) {
  133. CloseHandle (g_ComponentCancelEvent);
  134. g_ComponentCancelEvent = NULL;
  135. }
  136. if (g_SubComponentCancelEvent) {
  137. CloseHandle (g_SubComponentCancelEvent);
  138. g_SubComponentCancelEvent = NULL;
  139. }
  140. DeleteOurCriticalSection (&g_ProgBarCriticalSection);
  141. FreeGrowBuffer (&g_SliceArray);
  142. g_SliceCount = 0;
  143. g_MaxTickCount = 0;
  144. g_PaddingTicks = 0;
  145. g_CurrentTickCount = 0;
  146. g_CurrentSliceId = -1;
  147. g_Component = NULL;
  148. g_SubComponent = NULL;
  149. g_ReduceFactor = 1;
  150. SendMessage (g_ProgressBar, PBM_SETRANGE32, g_OrgRange.iLow, g_OrgRange.iHigh);
  151. g_ProgBarInitialized = FALSE;
  152. }
  153. UINT
  154. RegisterProgressBarSlice (
  155. IN UINT InitialEstimate
  156. )
  157. {
  158. PSLICE Slice;
  159. UINT SliceId;
  160. MYASSERT (g_ProgBarInitialized);
  161. if (!g_ProgBarInitialized) {
  162. return 0;
  163. }
  164. SliceId = g_SliceCount;
  165. Slice = (PSLICE) GrowBuffer (&g_SliceArray, sizeof (SLICE));
  166. g_SliceCount++;
  167. Slice->Started = FALSE;
  168. Slice->Completed = FALSE;
  169. Slice->TotalTicks = InitialEstimate * TICKSCALE;
  170. Slice->InitialGuess = Slice->TotalTicks;
  171. Slice->TicksSoFar = 0;
  172. Slice->LastTickDisplayed = 0;
  173. return SliceId;
  174. }
  175. VOID
  176. ReviseSliceEstimate (
  177. IN UINT SliceId,
  178. IN UINT RevisedEstimate
  179. )
  180. {
  181. PSLICE Slice;
  182. MYASSERT (g_ProgBarInitialized);
  183. if (!g_ProgBarInitialized) {
  184. return;
  185. }
  186. if (SliceId >= g_SliceCount) {
  187. DEBUGMSG ((DBG_WHOOPS, "ReviseSliceEstimate: Invalid slice ID %u", SliceId));
  188. return;
  189. }
  190. Slice = (PSLICE) g_SliceArray.Buf + SliceId;
  191. if (!g_CurrentTickCount) {
  192. Slice->TotalTicks = RevisedEstimate;
  193. return;
  194. }
  195. if (Slice->Completed) {
  196. DEBUGMSG ((DBG_WHOOPS, "ReviseSliceEstimate: Can't revise completed slice"));
  197. return;
  198. }
  199. if (Slice->InitialGuess == 0) {
  200. return;
  201. }
  202. RevisedEstimate *= TICKSCALE;
  203. MYASSERT (Slice->TicksSoFar * RevisedEstimate > Slice->TicksSoFar);
  204. MYASSERT (Slice->LastTickDisplayed * RevisedEstimate > Slice->LastTickDisplayed);
  205. Slice->TicksSoFar = (UINT) ((LONGLONG) Slice->TicksSoFar * (LONGLONG) RevisedEstimate / (LONGLONG) Slice->TotalTicks);
  206. Slice->LastTickDisplayed = (UINT) ((LONGLONG) Slice->LastTickDisplayed * (LONGLONG) RevisedEstimate / (LONGLONG) Slice->TotalTicks);
  207. Slice->TotalTicks = RevisedEstimate;
  208. }
  209. VOID
  210. BeginSliceProcessing (
  211. IN UINT SliceId
  212. )
  213. {
  214. PSLICE Slice;
  215. UINT u;
  216. UINT TotalTicks;
  217. MYASSERT (g_ProgBarInitialized);
  218. if (!g_ProgBarInitialized) {
  219. return;
  220. }
  221. if (!g_ProgressBar) {
  222. DEBUGMSG ((DBG_WHOOPS, "No progress bar handle"));
  223. return;
  224. }
  225. if (SliceId >= g_SliceCount) {
  226. DEBUGMSG ((DBG_WHOOPS, "BeginSliceProcessing: Invalid slice ID %u", SliceId));
  227. return;
  228. }
  229. if (!g_CurrentTickCount) {
  230. //
  231. // Initialize the progress bar
  232. //
  233. MYASSERT (g_CurrentSliceId == (UINT)-1);
  234. TotalTicks = 0;
  235. Slice = (PSLICE) g_SliceArray.Buf;
  236. for (u = 0 ; u < g_SliceCount ; u++) {
  237. TotalTicks += Slice->InitialGuess;
  238. Slice++;
  239. }
  240. TotalTicks /= TICKSCALE;
  241. g_PaddingTicks = TotalTicks * 5 / 100;
  242. g_MaxTickCount = TotalTicks + 2 * g_PaddingTicks;
  243. g_ReduceFactor = 1;
  244. while (g_MaxTickCount > 0xffff) {
  245. g_ReduceFactor *= 10;
  246. g_MaxTickCount /= 10;
  247. }
  248. SendMessage (g_ProgressBar, PBM_SETRANGE, 0, MAKELPARAM (0, g_MaxTickCount));
  249. SendMessage (g_ProgressBar, PBM_SETSTEP, 1, 0);
  250. if (g_Reverse) {
  251. SendMessage (
  252. g_ProgressBar,
  253. PBM_SETPOS,
  254. g_MaxTickCount - (g_PaddingTicks / g_ReduceFactor),
  255. 0
  256. );
  257. } else {
  258. SendMessage (g_ProgressBar, PBM_SETPOS, g_PaddingTicks / g_ReduceFactor, 0);
  259. }
  260. g_CurrentTickCount = g_PaddingTicks;
  261. g_CurrentPos = g_PaddingTicks;
  262. } else if (SliceId <= g_CurrentSliceId) {
  263. DEBUGMSG ((DBG_WHOOPS, "BeginSliceProcessing: Slice ID %u processed already", SliceId));
  264. return;
  265. }
  266. g_CurrentSliceId = SliceId;
  267. Slice = (PSLICE) g_SliceArray.Buf + g_CurrentSliceId;
  268. Slice->Started = TRUE;
  269. }
  270. VOID
  271. pIncrementBarIfNecessary (
  272. IN OUT PSLICE Slice
  273. )
  274. {
  275. UINT Increment;
  276. UINT Pos;
  277. if (Slice->TicksSoFar >= Slice->TotalTicks) {
  278. Slice->TicksSoFar = Slice->TotalTicks;
  279. Slice->Completed = TRUE;
  280. }
  281. if (Slice->TicksSoFar - Slice->LastTickDisplayed >= TICKSCALE) {
  282. Increment = (Slice->TicksSoFar - Slice->LastTickDisplayed) / TICKSCALE;
  283. Slice->LastTickDisplayed += Increment * TICKSCALE;
  284. if (g_Reverse) {
  285. Pos = ((g_CurrentPos + Slice->TicksSoFar) / TICKSCALE);
  286. Pos += g_PaddingTicks;
  287. Pos /= g_ReduceFactor;
  288. if (Pos > g_MaxTickCount) {
  289. Pos = g_MaxTickCount - (g_PaddingTicks / g_ReduceFactor);
  290. }
  291. SendMessage (g_ProgressBar, PBM_SETPOS, g_MaxTickCount - Pos, 0);
  292. } else {
  293. SendMessage (g_ProgressBar, PBM_DELTAPOS, Increment / g_ReduceFactor, 0);
  294. }
  295. }
  296. }
  297. VOID
  298. pTickProgressBar (
  299. IN UINT Ticks
  300. )
  301. {
  302. PSLICE Slice;
  303. LONGLONG x;
  304. if (!Ticks || g_CurrentSliceId == (UINT)-1 || g_CurrentSliceId >= g_SliceCount) {
  305. return;
  306. }
  307. Slice = (PSLICE) g_SliceArray.Buf + g_CurrentSliceId;
  308. if (!Slice->InitialGuess) {
  309. return;
  310. }
  311. if (Slice->Completed) {
  312. DEBUGMSG ((DBG_VERBOSE, "Progress slice ID %u already completed", g_CurrentSliceId));
  313. return;
  314. }
  315. MYASSERT (Ticks * TICKSCALE > Ticks);
  316. x = ((LONGLONG) Ticks * TICKSCALE * (LONGLONG) Slice->TotalTicks) / (LONGLONG) Slice->InitialGuess;
  317. MYASSERT (x + (LONGLONG) Slice->TicksSoFar < 0x100000000);
  318. Slice->TicksSoFar += (UINT) x;
  319. pIncrementBarIfNecessary (Slice);
  320. }
  321. BOOL
  322. TickProgressBarDelta (
  323. IN UINT TickCount
  324. )
  325. {
  326. BOOL rSuccess = TRUE;
  327. if (!g_ProgBarInitialized) {
  328. return TRUE;
  329. }
  330. if (g_CancelFlagPtr && *g_CancelFlagPtr) {
  331. SetLastError (ERROR_CANCELLED);
  332. rSuccess = FALSE;
  333. } else {
  334. pTickProgressBar (TickCount);
  335. }
  336. return rSuccess;
  337. }
  338. BOOL
  339. TickProgressBar (
  340. VOID
  341. )
  342. {
  343. if (!g_ProgBarInitialized) {
  344. return TRUE;
  345. }
  346. return TickProgressBarDelta (1);
  347. }
  348. VOID
  349. EndSliceProcessing (
  350. VOID
  351. )
  352. {
  353. PSLICE Slice;
  354. if (!g_ProgBarInitialized) {
  355. return;
  356. }
  357. Slice = (PSLICE) g_SliceArray.Buf + g_CurrentSliceId;
  358. if (!Slice->InitialGuess) {
  359. Slice->Completed = TRUE;
  360. return;
  361. }
  362. if (!Slice->Completed) {
  363. DEBUGMSG ((DBG_WARNING, "Progress bar slice %u was not completed.", g_CurrentSliceId));
  364. Slice->TicksSoFar = Slice->TotalTicks;
  365. Slice->Completed = TRUE;
  366. pIncrementBarIfNecessary (Slice);
  367. }
  368. g_CurrentPos += Slice->TotalTicks;
  369. if (g_CurrentSliceId == g_SliceCount - 1) {
  370. //
  371. // End of progress bar
  372. //
  373. SendMessage (g_ProgressBar, PBM_SETPOS, g_MaxTickCount, 0);
  374. }
  375. }
  376. BOOL
  377. pCheckProgressBarState (
  378. IN HANDLE CancelEvent
  379. )
  380. {
  381. SetEvent(CancelEvent);
  382. return (!g_CancelFlagPtr || !*g_CancelFlagPtr);
  383. }
  384. BOOL
  385. ProgressBar_SetWindowStringA(
  386. IN HWND Window,
  387. IN HANDLE CancelEvent,
  388. IN PCSTR Message, OPTIONAL
  389. IN DWORD MessageId OPTIONAL
  390. )
  391. {
  392. BOOL rSuccess = TRUE;
  393. PCSTR string = NULL;
  394. EnterOurCriticalSection (&g_ProgBarCriticalSection);
  395. if (g_ProgBarInitialized) {
  396. if (pCheckProgressBarState(CancelEvent)) {
  397. if (Message) {
  398. //
  399. // We have a normal message string.
  400. //
  401. if (!SetWindowTextA(Window, Message)) {
  402. rSuccess = FALSE;
  403. DEBUGMSG((DBG_ERROR,"ProgressBar: SetWindowText failed."));
  404. }
  405. }
  406. else if (MessageId) {
  407. //
  408. // We have a message ID. Convert it and set it.
  409. //
  410. string = GetStringResourceA(MessageId);
  411. if (string) {
  412. if (!SetWindowTextA(Window, string)) {
  413. rSuccess = FALSE;
  414. DEBUGMSG((DBG_ERROR,"ProgressBar: SetWindowText failed."));
  415. }
  416. FreeStringResourceA(string);
  417. }
  418. ELSE_DEBUGMSG((DBG_ERROR,"ProgressBar: Error with GetStringResource"));
  419. }
  420. else {
  421. //
  422. // Just clear the text.
  423. //
  424. if (!SetWindowTextA(Window, "")) {
  425. rSuccess = FALSE;
  426. DEBUGMSG((DBG_ERROR,"ProgressBar: SetWindowText failed."));
  427. }
  428. }
  429. }
  430. else {
  431. //
  432. // We are in a canceled state.
  433. //
  434. rSuccess = FALSE;
  435. SetLastError (ERROR_CANCELLED);
  436. }
  437. }
  438. LeaveOurCriticalSection (&g_ProgBarCriticalSection);
  439. return rSuccess;
  440. }
  441. DWORD
  442. pSetDelayedMessageA (
  443. IN PVOID Param
  444. )
  445. {
  446. DWORD rc = ERROR_SUCCESS;
  447. PDELAYTHREADPARAMS tParams = (PDELAYTHREADPARAMS) Param;
  448. //
  449. // Simply wait for the passed in delay or until someone signals the cancel
  450. // event.
  451. //
  452. switch (WaitForSingleObject(tParams -> CancelEvent, tParams -> Delay)) {
  453. case WAIT_TIMEOUT:
  454. //
  455. // We timed out without cancel being signaled. Set the delayed message.
  456. //
  457. ProgressBar_SetWindowStringA (
  458. tParams -> Window,
  459. tParams -> CancelEvent,
  460. tParams -> Message,
  461. tParams -> MessageId
  462. );
  463. break;
  464. case WAIT_OBJECT_0:
  465. default:
  466. //
  467. // We were canceled (or something strange happened :> Do nothing!
  468. //
  469. break;
  470. }
  471. //
  472. // can set a new thread now
  473. //
  474. tParams->InUse = FALSE;
  475. return rc;
  476. }
  477. VOID
  478. ProgressBar_CancelDelayedMessage (
  479. IN HANDLE CancelEvent
  480. )
  481. {
  482. if (!g_ProgBarInitialized) {
  483. return;
  484. }
  485. SetEvent(CancelEvent);
  486. }
  487. BOOL
  488. ProgressBar_SetDelayedMessageA (
  489. IN HWND Window,
  490. IN HANDLE CancelEvent,
  491. IN LPCSTR Message,
  492. IN DWORD MessageId,
  493. IN DWORD Delay
  494. )
  495. {
  496. BOOL rSuccess = FALSE;
  497. DWORD threadId;
  498. static DELAYTHREADPARAMS tParams;
  499. if (!g_ProgBarInitialized || tParams.InUse) {
  500. return TRUE;
  501. }
  502. if (!pCheckProgressBarState(Window)) {
  503. //
  504. // Fill in the parameters for this call to create thread.
  505. //
  506. tParams.Window = Window;
  507. tParams.CancelEvent = CancelEvent;
  508. tParams.Message = Message;
  509. tParams.MessageId = MessageId;
  510. tParams.Delay = Delay;
  511. //
  512. // Spawn off a thread that will set the message.
  513. //
  514. rSuccess = NULL != CreateThread (
  515. NULL, // No inheritance.
  516. 0, // Normal stack size.
  517. pSetDelayedMessageA,
  518. &tParams,
  519. 0, // Run immediately.
  520. &threadId
  521. );
  522. if (rSuccess) {
  523. tParams.InUse = TRUE;
  524. }
  525. ELSE_DEBUGMSG((DBG_ERROR,"Error spawning thread in ProgressBar_SetDelayedMessageA."));
  526. }
  527. return rSuccess;
  528. }
  529. #if 0
  530. DWORD
  531. pTickProgressBarThread (
  532. IN PVOID Param
  533. )
  534. {
  535. DWORD rc = ERROR_SUCCESS;
  536. PTICKTHREADPARAMS Params = (PTICKTHREADPARAMS)Param;
  537. BOOL Continue = TRUE;
  538. //
  539. // Simply wait for the passed in delay or until someone signals the cancel
  540. // event.
  541. //
  542. do {
  543. switch (WaitForSingleObject(Params->CancelEvent, Params->TickCount)) {
  544. case WAIT_TIMEOUT:
  545. //
  546. // We timed out without cancel being signaled. Tick the progress bar.
  547. //
  548. if (!TickProgressBarDelta (Params->TickCount)) {
  549. //
  550. // cancelled
  551. //
  552. Continue = FALSE;
  553. }
  554. break;
  555. case WAIT_OBJECT_0:
  556. default:
  557. //
  558. // We were canceled (or something strange happened :> Do nothing!
  559. //
  560. Continue = FALSE;
  561. break;
  562. }
  563. } while (Continue);
  564. //
  565. // can set a new thread now
  566. //
  567. Params->InUse = FALSE;
  568. return rc;
  569. }
  570. BOOL
  571. ProgressBar_CreateTickThread (
  572. IN HANDLE CancelEvent,
  573. IN DWORD TickCount
  574. )
  575. {
  576. BOOL rSuccess = FALSE;
  577. DWORD threadId;
  578. static TICKTHREADPARAMS g_Params;
  579. if (g_ProgBarInitialized && !g_Params.InUse) {
  580. if (pCheckProgressBarState(NULL)) {
  581. //
  582. // Fill in the parameters for this call to create thread.
  583. //
  584. g_Params.CancelEvent = CancelEvent;
  585. g_Params.TickCount = TickCount;
  586. //
  587. // Spawn off a thread that will set the message.
  588. //
  589. if (CreateThread (
  590. NULL, // No inheritance.
  591. 0, // Normal stack size.
  592. pTickProgressBarThread,
  593. &g_Params,
  594. 0, // Run immediately.
  595. &threadId
  596. )) {
  597. rSuccess = TRUE;
  598. g_Params.InUse = TRUE;
  599. }
  600. ELSE_DEBUGMSG ((DBG_ERROR, "Error spawning thread in ProgressBar_CreateTickThread."));
  601. }
  602. }
  603. return rSuccess;
  604. }
  605. BOOL
  606. ProgressBar_CancelTickThread (
  607. IN HANDLE CancelEvent
  608. )
  609. {
  610. if (!g_ProgBarInitialized) {
  611. return TRUE;
  612. }
  613. return SetEvent(CancelEvent);
  614. }
  615. #endif