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.

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