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.

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