1 // ProgressDialog2.cpp
2 
3 #include "StdAfx.h"
4 
5 #ifdef Z7_OLD_WIN_SDK
6 #include <ShlGuid.h>
7 #endif
8 
9 #include "../../../Common/IntToString.h"
10 #include "../../../Common/StringConvert.h"
11 
12 #include "../../../Windows/Clipboard.h"
13 #include "../../../Windows/ErrorMsg.h"
14 
15 #include "../GUI/ExtractRes.h"
16 
17 #include "LangUtils.h"
18 
19 #include "DialogSize.h"
20 #include "ProgressDialog2.h"
21 #include "ProgressDialog2Res.h"
22 
23 using namespace NWindows;
24 
25 extern HINSTANCE g_hInstance;
26 
27 static const UINT_PTR kTimerID = 3;
28 
29 static const UINT kCloseMessage = WM_APP + 1;
30 // we can't use WM_USER, since WM_USER can be used by standard Windows procedure for Dialog
31 
32 static const UINT kTimerElapse =
33   #ifdef UNDER_CE
34   500
35   #else
36   200
37   #endif
38   ;
39 
40 static const UINT kCreateDelay =
41   #ifdef UNDER_CE
42   2500
43   #else
44   500
45   #endif
46   ;
47 
48 static const DWORD kPauseSleepTime = 100;
49 
50 #ifdef Z7_LANG
51 
52 static const UInt32 kLangIDs[] =
53 {
54   IDT_PROGRESS_ELAPSED,
55   IDT_PROGRESS_REMAINING,
56   IDT_PROGRESS_TOTAL,
57   IDT_PROGRESS_SPEED,
58   IDT_PROGRESS_PROCESSED,
59   IDT_PROGRESS_RATIO,
60   IDT_PROGRESS_ERRORS,
61   IDB_PROGRESS_BACKGROUND,
62   IDB_PAUSE
63 };
64 
65 static const UInt32 kLangIDs_Colon[] =
66 {
67   IDT_PROGRESS_PACKED,
68   IDT_PROGRESS_FILES
69 };
70 
71 #endif
72 
73 
74 #define UNDEFINED_VAL         ((UInt64)(Int64)-1)
75 #define INIT_AS_UNDEFINED(v)  v = UNDEFINED_VAL;
76 #define IS_UNDEFINED_VAL(v)   ((v) == UNDEFINED_VAL)
77 #define IS_DEFINED_VAL(v)     ((v) != UNDEFINED_VAL)
78 
CProgressSync()79 CProgressSync::CProgressSync():
80     _stopped(false), _paused(false),
81     _bytesProgressMode(true),
82     _isDir(false),
83     _totalBytes(UNDEFINED_VAL), _completedBytes(0),
84     _totalFiles(UNDEFINED_VAL), _curFiles(0),
85     _inSize(UNDEFINED_VAL),
86     _outSize(UNDEFINED_VAL)
87     {}
88 
89 #define CHECK_STOP  if (_stopped) return E_ABORT; if (!_paused) return S_OK;
90 #define CRITICAL_LOCK NSynchronization::CCriticalSectionLock lock(_cs);
91 
Get_Paused()92 bool CProgressSync::Get_Paused()
93 {
94   CRITICAL_LOCK
95   return _paused;
96 }
97 
CheckStop()98 HRESULT CProgressSync::CheckStop()
99 {
100   for (;;)
101   {
102     {
103       CRITICAL_LOCK
104       CHECK_STOP
105     }
106     ::Sleep(kPauseSleepTime);
107   }
108 }
109 
ScanProgress(UInt64 numFiles, UInt64 totalSize, const FString &fileName, bool isDir)110 HRESULT CProgressSync::ScanProgress(UInt64 numFiles, UInt64 totalSize, const FString &fileName, bool isDir)
111 {
112   {
113     CRITICAL_LOCK
114     _totalFiles = numFiles;
115     _totalBytes = totalSize;
116     _filePath = fs2us(fileName);
117     _isDir = isDir;
118     // _completedBytes = 0;
119     CHECK_STOP
120   }
121   return CheckStop();
122 }
123 
Set_NumFilesTotal(UInt64 val)124 HRESULT CProgressSync::Set_NumFilesTotal(UInt64 val)
125 {
126   {
127     CRITICAL_LOCK
128     _totalFiles = val;
129     CHECK_STOP
130   }
131   return CheckStop();
132 }
133 
Set_NumBytesTotal(UInt64 val)134 void CProgressSync::Set_NumBytesTotal(UInt64 val)
135 {
136   CRITICAL_LOCK
137   _totalBytes = val;
138 }
139 
Set_NumFilesCur(UInt64 val)140 void CProgressSync::Set_NumFilesCur(UInt64 val)
141 {
142   CRITICAL_LOCK
143   _curFiles = val;
144 }
145 
Set_NumBytesCur(const UInt64 *val)146 HRESULT CProgressSync::Set_NumBytesCur(const UInt64 *val)
147 {
148   {
149     CRITICAL_LOCK
150     if (val)
151       _completedBytes = *val;
152     CHECK_STOP
153   }
154   return CheckStop();
155 }
156 
Set_NumBytesCur(UInt64 val)157 HRESULT CProgressSync::Set_NumBytesCur(UInt64 val)
158 {
159   {
160     CRITICAL_LOCK
161     _completedBytes = val;
162     CHECK_STOP
163   }
164   return CheckStop();
165 }
166 
Set_Ratio(const UInt64 *inSize, const UInt64 *outSize)167 void CProgressSync::Set_Ratio(const UInt64 *inSize, const UInt64 *outSize)
168 {
169   CRITICAL_LOCK
170   if (inSize)
171     _inSize = *inSize;
172   if (outSize)
173     _outSize = *outSize;
174 }
175 
Set_TitleFileName(const UString &fileName)176 void CProgressSync::Set_TitleFileName(const UString &fileName)
177 {
178   CRITICAL_LOCK
179   _titleFileName = fileName;
180 }
181 
Set_Status(const UString &s)182 void CProgressSync::Set_Status(const UString &s)
183 {
184   CRITICAL_LOCK
185   _status = s;
186 }
187 
Set_Status2(const UString &s, const wchar_t *path, bool isDir)188 HRESULT CProgressSync::Set_Status2(const UString &s, const wchar_t *path, bool isDir)
189 {
190   {
191     CRITICAL_LOCK
192     _status = s;
193     if (path)
194       _filePath = path;
195     else
196       _filePath.Empty();
197     _isDir = isDir;
198   }
199   return CheckStop();
200 }
201 
Set_FilePath(const wchar_t *path, bool isDir)202 void CProgressSync::Set_FilePath(const wchar_t *path, bool isDir)
203 {
204   CRITICAL_LOCK
205   if (path)
206     _filePath = path;
207   else
208     _filePath.Empty();
209   _isDir = isDir;
210 }
211 
212 
AddError_Message(const wchar_t *message)213 void CProgressSync::AddError_Message(const wchar_t *message)
214 {
215   CRITICAL_LOCK
216   Messages.Add(message);
217 }
218 
AddError_Message_Name(const wchar_t *message, const wchar_t *name)219 void CProgressSync::AddError_Message_Name(const wchar_t *message, const wchar_t *name)
220 {
221   UString s;
222   if (name && *name != 0)
223     s += name;
224   if (message && *message != 0)
225   {
226     if (!s.IsEmpty())
227       s.Add_LF();
228     s += message;
229     if (!s.IsEmpty() && s.Back() == L'\n')
230       s.DeleteBack();
231   }
232   AddError_Message(s);
233 }
234 
AddError_Code_Name(HRESULT systemError, const wchar_t *name)235 void CProgressSync::AddError_Code_Name(HRESULT systemError, const wchar_t *name)
236 {
237   UString s = NError::MyFormatMessage(systemError);
238   if (systemError == 0)
239     s = "Error";
240   AddError_Message_Name(s, name);
241 }
242 
CProgressDialog()243 CProgressDialog::CProgressDialog():
244    _timer(0),
245    CompressingMode(true),
246    MainWindow(NULL)
247 {
248   _isDir = false;
249 
250   _numMessages = 0;
251   IconID = -1;
252   MessagesDisplayed = false;
253   _wasCreated = false;
254   _needClose = false;
255   _inCancelMessageBox = false;
256   _externalCloseMessageWasReceived = false;
257 
258   _numPostedMessages = 0;
259   _numAutoSizeMessages = 0;
260   _errorsWereDisplayed = false;
261   _waitCloseByCancelButton = false;
262   _cancelWasPressed = false;
263   ShowCompressionInfo = true;
264   WaitMode = false;
265   if (_dialogCreatedEvent.Create() != S_OK)
266     throw 1334987;
267   if (_createDialogEvent.Create() != S_OK)
268     throw 1334987;
269   // #ifdef __ITaskbarList3_INTERFACE_DEFINED__
270   CoCreateInstance(CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER, IID_ITaskbarList3, (void**)&_taskbarList);
271   if (_taskbarList)
272     _taskbarList->HrInit();
273   // #endif
274 }
275 
276 #ifndef Z7_SFX
277 
~CProgressDialog()278 CProgressDialog::~CProgressDialog()
279 {
280   // #ifdef __ITaskbarList3_INTERFACE_DEFINED__
281   SetTaskbarProgressState(TBPF_NOPROGRESS);
282   // #endif
283   AddToTitle(L"");
284 }
AddToTitle(LPCWSTR s)285 void CProgressDialog::AddToTitle(LPCWSTR s)
286 {
287   if (MainWindow)
288   {
289     CWindow window(MainWindow);
290     window.SetText((UString)s + MainTitle);
291   }
292 }
293 
294 #endif
295 
296 
SetTaskbarProgressState()297 void CProgressDialog::SetTaskbarProgressState()
298 {
299   // #ifdef __ITaskbarList3_INTERFACE_DEFINED__
300   if (_taskbarList && _hwndForTaskbar)
301   {
302     TBPFLAG tbpFlags;
303     if (Sync.Get_Paused())
304       tbpFlags = TBPF_PAUSED;
305     else
306       tbpFlags = _errorsWereDisplayed ? TBPF_ERROR: TBPF_NORMAL;
307     SetTaskbarProgressState(tbpFlags);
308   }
309   // #endif
310 }
311 
312 static const unsigned kTitleFileNameSizeLimit = 36;
313 static const unsigned kCurrentFileNameSizeLimit = 82;
314 
ReduceString(UString &s, unsigned size)315 static void ReduceString(UString &s, unsigned size)
316 {
317   if (s.Len() <= size)
318     return;
319   s.Delete(size / 2, s.Len() - size);
320   s.Insert(size / 2, L" ... ");
321 }
322 
EnableErrorsControls(bool enable)323 void CProgressDialog::EnableErrorsControls(bool enable)
324 {
325   ShowItem_Bool(IDT_PROGRESS_ERRORS, enable);
326   ShowItem_Bool(IDT_PROGRESS_ERRORS_VAL, enable);
327   ShowItem_Bool(IDL_PROGRESS_MESSAGES, enable);
328 }
329 
OnInit()330 bool CProgressDialog::OnInit()
331 {
332   _hwndForTaskbar = MainWindow;
333   if (!_hwndForTaskbar)
334     _hwndForTaskbar = GetParent();
335   if (!_hwndForTaskbar)
336     _hwndForTaskbar = *this;
337 
338   INIT_AS_UNDEFINED(_progressBar_Range)
339   INIT_AS_UNDEFINED(_progressBar_Pos)
340 
341   INIT_AS_UNDEFINED(_prevPercentValue)
342   INIT_AS_UNDEFINED(_prevElapsedSec)
343   INIT_AS_UNDEFINED(_prevRemainingSec)
344 
345   INIT_AS_UNDEFINED(_prevSpeed)
346   _prevSpeed_MoveBits = 0;
347 
348   _prevTime = ::GetTickCount();
349   _elapsedTime = 0;
350 
351   INIT_AS_UNDEFINED(_totalBytes_Prev)
352   INIT_AS_UNDEFINED(_processed_Prev)
353   INIT_AS_UNDEFINED(_packed_Prev)
354   INIT_AS_UNDEFINED(_ratio_Prev)
355 
356   _filesStr_Prev.Empty();
357   _filesTotStr_Prev.Empty();
358 
359   _foreground = true;
360 
361   m_ProgressBar.Attach(GetItem(IDC_PROGRESS1));
362   _messageList.Attach(GetItem(IDL_PROGRESS_MESSAGES));
363   _messageList.SetUnicodeFormat();
364   _messageList.SetExtendedListViewStyle(LVS_EX_FULLROWSELECT);
365 
366   _wasCreated = true;
367   _dialogCreatedEvent.Set();
368 
369   #ifdef Z7_LANG
370   LangSetDlgItems(*this, kLangIDs, Z7_ARRAY_SIZE(kLangIDs));
371   LangSetDlgItems_Colon(*this, kLangIDs_Colon, Z7_ARRAY_SIZE(kLangIDs_Colon));
372   #endif
373 
374   CWindow window(GetItem(IDB_PROGRESS_BACKGROUND));
375   window.GetText(_background_String);
376   _backgrounded_String = _background_String;
377   _backgrounded_String.RemoveChar(L'&');
378 
379   window = GetItem(IDB_PAUSE);
380   window.GetText(_pause_String);
381 
382   LangString(IDS_PROGRESS_FOREGROUND, _foreground_String);
383   LangString(IDS_CONTINUE, _continue_String);
384   LangString(IDS_PROGRESS_PAUSED, _paused_String);
385 
386   SetText(_title);
387   SetPauseText();
388   SetPriorityText();
389 
390   _messageList.InsertColumn(0, L"", 30);
391   _messageList.InsertColumn(1, L"", 600);
392 
393   _messageList.SetColumnWidthAuto(0);
394   _messageList.SetColumnWidthAuto(1);
395 
396   EnableErrorsControls(false);
397 
398   GetItemSizes(IDCANCEL, _buttonSizeX, _buttonSizeY);
399   _numReduceSymbols = kCurrentFileNameSizeLimit;
400   NormalizeSize(true);
401 
402   if (!ShowCompressionInfo)
403   {
404     HideItem(IDT_PROGRESS_PACKED);
405     HideItem(IDT_PROGRESS_PACKED_VAL);
406     HideItem(IDT_PROGRESS_RATIO);
407     HideItem(IDT_PROGRESS_RATIO_VAL);
408   }
409 
410   if (IconID >= 0)
411   {
412     HICON icon = LoadIcon(g_hInstance, MAKEINTRESOURCE(IconID));
413     // SetIcon(ICON_SMALL, icon);
414     SetIcon(ICON_BIG, icon);
415   }
416   _timer = SetTimer(kTimerID, kTimerElapse);
417   #ifdef UNDER_CE
418   Foreground();
419   #endif
420 
421   CheckNeedClose();
422 
423   SetTaskbarProgressState();
424 
425   return CModalDialog::OnInit();
426 }
427 
428 static const UINT kIDs[] =
429 {
430   IDT_PROGRESS_ELAPSED,   IDT_PROGRESS_ELAPSED_VAL,
431   IDT_PROGRESS_REMAINING, IDT_PROGRESS_REMAINING_VAL,
432   IDT_PROGRESS_FILES,     IDT_PROGRESS_FILES_VAL,
433   0,                      IDT_PROGRESS_FILES_TOTAL,
434   IDT_PROGRESS_ERRORS,    IDT_PROGRESS_ERRORS_VAL,
435 
436   IDT_PROGRESS_TOTAL,     IDT_PROGRESS_TOTAL_VAL,
437   IDT_PROGRESS_SPEED,     IDT_PROGRESS_SPEED_VAL,
438   IDT_PROGRESS_PROCESSED, IDT_PROGRESS_PROCESSED_VAL,
439   IDT_PROGRESS_PACKED,    IDT_PROGRESS_PACKED_VAL,
440   IDT_PROGRESS_RATIO,     IDT_PROGRESS_RATIO_VAL
441 };
442 
OnSize(WPARAM , int xSize, int ySize)443 bool CProgressDialog::OnSize(WPARAM /* wParam */, int xSize, int ySize)
444 {
445   int sY;
446   int sStep;
447   int mx, my;
448   {
449     RECT r;
450     GetClientRectOfItem(IDT_PROGRESS_ELAPSED, r);
451     mx = r.left;
452     my = r.top;
453     sY = RECT_SIZE_Y(r);
454     GetClientRectOfItem(IDT_PROGRESS_REMAINING, r);
455     sStep = r.top - my;
456   }
457 
458   InvalidateRect(NULL);
459 
460   const int xSizeClient = xSize - mx * 2;
461 
462   {
463     unsigned i;
464     for (i = 800; i > 40; i = i * 9 / 10)
465       if (Units_To_Pixels_X((int)i) <= xSizeClient)
466         break;
467     _numReduceSymbols = i / 4;
468   }
469 
470   int yPos = ySize - my - _buttonSizeY;
471 
472   ChangeSubWindowSizeX(GetItem(IDT_PROGRESS_STATUS), xSize - mx * 2);
473   ChangeSubWindowSizeX(GetItem(IDT_PROGRESS_FILE_NAME), xSize - mx * 2);
474   ChangeSubWindowSizeX(GetItem(IDC_PROGRESS1), xSize - mx * 2);
475 
476   int bSizeX = _buttonSizeX;
477   int mx2 = mx;
478   for (;; mx2--)
479   {
480     const int bSize2 = bSizeX * 3 + mx2 * 2;
481     if (bSize2 <= xSizeClient)
482       break;
483     if (mx2 < 5)
484     {
485       bSizeX = (xSizeClient - mx2 * 2) / 3;
486       break;
487     }
488   }
489   if (bSizeX < 2)
490     bSizeX = 2;
491 
492   {
493     RECT r;
494     GetClientRectOfItem(IDL_PROGRESS_MESSAGES, r);
495     const int y = r.top;
496     int ySize2 = yPos - my - y;
497     const int kMinYSize = _buttonSizeY + _buttonSizeY * 3 / 4;
498     int xx = xSize - mx * 2;
499     if (ySize2 < kMinYSize)
500     {
501       ySize2 = kMinYSize;
502       if (xx > bSizeX * 2)
503         xx -= bSizeX;
504     }
505 
506     _messageList.Move(mx, y, xx, ySize2);
507   }
508 
509   {
510     int xPos = xSize - mx;
511     xPos -= bSizeX;
512     MoveItem(IDCANCEL, xPos, yPos, bSizeX, _buttonSizeY);
513     xPos -= (mx2 + bSizeX);
514     MoveItem(IDB_PAUSE, xPos, yPos, bSizeX, _buttonSizeY);
515     xPos -= (mx2 + bSizeX);
516     MoveItem(IDB_PROGRESS_BACKGROUND, xPos, yPos, bSizeX, _buttonSizeY);
517   }
518 
519   int valueSize;
520   int labelSize;
521   int padSize;
522 
523   labelSize = Units_To_Pixels_X(MY_PROGRESS_LABEL_UNITS_MIN);
524   valueSize = Units_To_Pixels_X(MY_PROGRESS_VAL_UNITS);
525   padSize = Units_To_Pixels_X(MY_PROGRESS_PAD_UNITS);
526   const int requiredSize = (labelSize + valueSize) * 2 + padSize;
527 
528   int gSize;
529   {
530     if (requiredSize < xSizeClient)
531     {
532       const int incr = (xSizeClient - requiredSize) / 3;
533       labelSize += incr;
534     }
535     else
536       labelSize = (xSizeClient - valueSize * 2 - padSize) / 2;
537     if (labelSize < 0)
538       labelSize = 0;
539 
540     gSize = labelSize + valueSize;
541     padSize = xSizeClient - gSize * 2;
542   }
543 
544   labelSize = gSize - valueSize;
545 
546   yPos = my;
547   for (unsigned i = 0; i < Z7_ARRAY_SIZE(kIDs); i += 2)
548   {
549     int x = mx;
550     const unsigned kNumColumn1Items = 5 * 2;
551     if (i >= kNumColumn1Items)
552     {
553       if (i == kNumColumn1Items)
554         yPos = my;
555       x = mx + gSize + padSize;
556     }
557     if (kIDs[i] != 0)
558     MoveItem(kIDs[i], x, yPos, labelSize, sY);
559     MoveItem(kIDs[i + 1], x + labelSize, yPos, valueSize, sY);
560     yPos += sStep;
561   }
562   return false;
563 }
564 
OnCancel()565 void CProgressDialog::OnCancel() { Sync.Set_Stopped(true); }
OnOK()566 void CProgressDialog::OnOK() { }
567 
SetProgressRange(UInt64 range)568 void CProgressDialog::SetProgressRange(UInt64 range)
569 {
570   if (range == _progressBar_Range)
571     return;
572   _progressBar_Range = range;
573   INIT_AS_UNDEFINED(_progressBar_Pos)
574   _progressConv.Init(range);
575   m_ProgressBar.SetRange32(0, _progressConv.Count(range));
576 }
577 
SetProgressPos(UInt64 pos)578 void CProgressDialog::SetProgressPos(UInt64 pos)
579 {
580   if (pos >= _progressBar_Range ||
581       pos <= _progressBar_Pos ||
582       pos - _progressBar_Pos >= (_progressBar_Range >> 10))
583   {
584     m_ProgressBar.SetPos(_progressConv.Count(pos));
585     // #ifdef __ITaskbarList3_INTERFACE_DEFINED__
586     if (_taskbarList && _hwndForTaskbar)
587       _taskbarList->SetProgressValue(_hwndForTaskbar, pos, _progressBar_Range);
588     // #endif
589     _progressBar_Pos = pos;
590   }
591 }
592 
593 #define UINT_TO_STR_2(val) { s[0] = (wchar_t)('0' + (val) / 10); s[1] = (wchar_t)('0' + (val) % 10); s += 2; }
594 
595 void GetTimeString(UInt64 timeValue, wchar_t *s);
GetTimeString(UInt64 timeValue, wchar_t *s)596 void GetTimeString(UInt64 timeValue, wchar_t *s)
597 {
598   UInt64 hours = timeValue / 3600;
599   UInt32 seconds = (UInt32)(timeValue - hours * 3600);
600   UInt32 minutes = seconds / 60;
601   seconds %= 60;
602   if (hours > 99)
603   {
604     ConvertUInt64ToString(hours, s);
605     for (; *s != 0; s++);
606   }
607   else
608   {
609     UInt32 hours32 = (UInt32)hours;
610     UINT_TO_STR_2(hours32)
611   }
612   *s++ = ':'; UINT_TO_STR_2(minutes)
613   *s++ = ':'; UINT_TO_STR_2(seconds)
614   *s = 0;
615 }
616 
ConvertSizeToString(UInt64 v, wchar_t *s)617 static void ConvertSizeToString(UInt64 v, wchar_t *s)
618 {
619   Byte c = 0;
620        if (v >= ((UInt64)100000 << 20)) { v >>= 30; c = 'G'; }
621   else if (v >= ((UInt64)100000 << 10)) { v >>= 20; c = 'M'; }
622   else if (v >= ((UInt64)100000 <<  0)) { v >>= 10; c = 'K'; }
623   ConvertUInt64ToString(v, s);
624   if (c != 0)
625   {
626     s += MyStringLen(s);
627     *s++ = ' ';
628     *s++ = c;
629     *s++ = 'B';
630     *s++ = 0;
631   }
632 }
633 
ShowSize(unsigned id, UInt64 val, UInt64 &prev)634 void CProgressDialog::ShowSize(unsigned id, UInt64 val, UInt64 &prev)
635 {
636   if (val == prev)
637     return;
638   prev = val;
639   wchar_t s[40];
640   s[0] = 0;
641   if (IS_DEFINED_VAL(val))
642     ConvertSizeToString(val, s);
643   SetItemText(id, s);
644 }
645 
GetChangedString(const UString &newStr, UString &prevStr, bool &hasChanged)646 static void GetChangedString(const UString &newStr, UString &prevStr, bool &hasChanged)
647 {
648   hasChanged = !(prevStr == newStr);
649   if (hasChanged)
650     prevStr = newStr;
651 }
652 
GetPower32(UInt32 val)653 static unsigned GetPower32(UInt32 val)
654 {
655   const unsigned kStart = 32;
656   UInt32 mask = ((UInt32)1 << (kStart - 1));
657   for (unsigned i = kStart;; i--)
658   {
659     if (i == 0 || (val & mask) != 0)
660       return i;
661     mask >>= 1;
662   }
663 }
664 
GetPower64(UInt64 val)665 static unsigned GetPower64(UInt64 val)
666 {
667   UInt32 high = (UInt32)(val >> 32);
668   if (high == 0)
669     return GetPower32((UInt32)val);
670   return GetPower32(high) + 32;
671 }
672 
MyMultAndDiv(UInt64 mult1, UInt64 mult2, UInt64 divider)673 static UInt64 MyMultAndDiv(UInt64 mult1, UInt64 mult2, UInt64 divider)
674 {
675   unsigned pow1 = GetPower64(mult1);
676   unsigned pow2 = GetPower64(mult2);
677   while (pow1 + pow2 > 64)
678   {
679     if (pow1 > pow2) { pow1--; mult1 >>= 1; }
680     else             { pow2--; mult2 >>= 1; }
681     divider >>= 1;
682   }
683   UInt64 res = mult1 * mult2;
684   if (divider != 0)
685     res /= divider;
686   return res;
687 }
688 
UpdateStatInfo(bool showAll)689 void CProgressDialog::UpdateStatInfo(bool showAll)
690 {
691   UInt64 total, completed, totalFiles, completedFiles, inSize, outSize;
692   bool bytesProgressMode;
693 
694   bool titleFileName_Changed;
695   bool curFilePath_Changed;
696   bool status_Changed;
697   unsigned numErrors;
698   {
699     NSynchronization::CCriticalSectionLock lock(Sync._cs);
700     total = Sync._totalBytes;
701     completed = Sync._completedBytes;
702     totalFiles = Sync._totalFiles;
703     completedFiles = Sync._curFiles;
704     inSize = Sync._inSize;
705     outSize = Sync._outSize;
706     bytesProgressMode = Sync._bytesProgressMode;
707 
708     GetChangedString(Sync._titleFileName, _titleFileName, titleFileName_Changed);
709     GetChangedString(Sync._filePath, _filePath, curFilePath_Changed);
710     GetChangedString(Sync._status, _status, status_Changed);
711     if (_isDir != Sync._isDir)
712     {
713       curFilePath_Changed = true;
714       _isDir = Sync._isDir;
715     }
716     numErrors = Sync.Messages.Size();
717   }
718 
719   UInt32 curTime = ::GetTickCount();
720 
721   const UInt64 progressTotal = bytesProgressMode ? total : totalFiles;
722   const UInt64 progressCompleted = bytesProgressMode ? completed : completedFiles;
723   {
724     if (IS_UNDEFINED_VAL(progressTotal))
725     {
726       // SetPos(0);
727       // SetRange(progressCompleted);
728     }
729     else
730     {
731       if (_progressBar_Pos != 0 || progressCompleted != 0 ||
732           (_progressBar_Range == 0 && progressTotal != 0))
733       {
734         SetProgressRange(progressTotal);
735         SetProgressPos(progressCompleted);
736       }
737     }
738   }
739 
740   ShowSize(IDT_PROGRESS_TOTAL_VAL, total, _totalBytes_Prev);
741 
742   _elapsedTime += (curTime - _prevTime);
743   _prevTime = curTime;
744   UInt64 elapsedSec = _elapsedTime / 1000;
745   bool elapsedChanged = false;
746   if (elapsedSec != _prevElapsedSec)
747   {
748     _prevElapsedSec = elapsedSec;
749     elapsedChanged = true;
750     wchar_t s[40];
751     GetTimeString(elapsedSec, s);
752     SetItemText(IDT_PROGRESS_ELAPSED_VAL, s);
753   }
754 
755   bool needSetTitle = false;
756   if (elapsedChanged || showAll)
757   {
758     if (numErrors > _numPostedMessages)
759     {
760       UpdateMessagesDialog();
761       wchar_t s[32];
762       ConvertUInt64ToString(numErrors, s);
763       SetItemText(IDT_PROGRESS_ERRORS_VAL, s);
764       if (!_errorsWereDisplayed)
765       {
766         _errorsWereDisplayed = true;
767         EnableErrorsControls(true);
768         SetTaskbarProgressState();
769       }
770     }
771 
772     if (progressCompleted != 0)
773     {
774       if (IS_UNDEFINED_VAL(progressTotal))
775       {
776         if (IS_DEFINED_VAL(_prevRemainingSec))
777         {
778           INIT_AS_UNDEFINED(_prevRemainingSec)
779           SetItemText(IDT_PROGRESS_REMAINING_VAL, L"");
780         }
781       }
782       else
783       {
784         UInt64 remainingTime = 0;
785         if (progressCompleted < progressTotal)
786           remainingTime = MyMultAndDiv(_elapsedTime, progressTotal - progressCompleted, progressCompleted);
787         UInt64 remainingSec = remainingTime / 1000;
788         if (remainingSec != _prevRemainingSec)
789         {
790           _prevRemainingSec = remainingSec;
791           wchar_t s[40];
792           GetTimeString(remainingSec, s);
793           SetItemText(IDT_PROGRESS_REMAINING_VAL, s);
794         }
795       }
796       {
797         const UInt64 elapsedTime = (_elapsedTime == 0) ? 1 : _elapsedTime;
798         // 22.02: progressCompleted can be for number of files
799         UInt64 v = (completed * 1000) / elapsedTime;
800         Byte c = 0;
801         unsigned moveBits = 0;
802              if (v >= ((UInt64)10000 << 10)) { moveBits = 20; c = 'M'; }
803         else if (v >= ((UInt64)10000 <<  0)) { moveBits = 10; c = 'K'; }
804         v >>= moveBits;
805         if (moveBits != _prevSpeed_MoveBits || v != _prevSpeed)
806         {
807           _prevSpeed_MoveBits = moveBits;
808           _prevSpeed = v;
809           wchar_t s[40];
810           ConvertUInt64ToString(v, s);
811           unsigned pos = MyStringLen(s);
812           s[pos++] = ' ';
813           if (moveBits != 0)
814             s[pos++] = c;
815           s[pos++] = 'B';
816           s[pos++] = '/';
817           s[pos++] = 's';
818           s[pos++] = 0;
819           SetItemText(IDT_PROGRESS_SPEED_VAL, s);
820         }
821       }
822     }
823 
824     {
825       UInt64 percent = 0;
826       {
827         if (IS_DEFINED_VAL(progressTotal))
828         {
829           percent = progressCompleted * 100;
830           if (progressTotal != 0)
831             percent /= progressTotal;
832         }
833       }
834       if (percent != _prevPercentValue)
835       {
836         _prevPercentValue = percent;
837         needSetTitle = true;
838       }
839     }
840 
841     {
842       wchar_t s[64];
843 
844       ConvertUInt64ToString(completedFiles, s);
845       if (_filesStr_Prev != s)
846       {
847         _filesStr_Prev = s;
848         SetItemText(IDT_PROGRESS_FILES_VAL, s);
849       }
850 
851       s[0] = 0;
852       if (IS_DEFINED_VAL(totalFiles))
853       {
854         MyStringCopy(s, L" / ");
855         ConvertUInt64ToString(totalFiles, s + MyStringLen(s));
856       }
857       if (_filesTotStr_Prev != s)
858       {
859         _filesTotStr_Prev = s;
860         SetItemText(IDT_PROGRESS_FILES_TOTAL, s);
861       }
862     }
863 
864     const UInt64 packSize   = CompressingMode ? outSize : inSize;
865     const UInt64 unpackSize = CompressingMode ? inSize : outSize;
866 
867     if (IS_UNDEFINED_VAL(unpackSize) &&
868         IS_UNDEFINED_VAL(packSize))
869     {
870       ShowSize(IDT_PROGRESS_PROCESSED_VAL, completed, _processed_Prev);
871       ShowSize(IDT_PROGRESS_PACKED_VAL, UNDEFINED_VAL, _packed_Prev);
872     }
873     else
874     {
875       ShowSize(IDT_PROGRESS_PROCESSED_VAL, unpackSize, _processed_Prev);
876       ShowSize(IDT_PROGRESS_PACKED_VAL, packSize, _packed_Prev);
877 
878       if (IS_DEFINED_VAL(packSize) &&
879           IS_DEFINED_VAL(unpackSize) &&
880           unpackSize != 0)
881       {
882         wchar_t s[32];
883         UInt64 ratio = packSize * 100 / unpackSize;
884         if (_ratio_Prev != ratio)
885         {
886           _ratio_Prev = ratio;
887           ConvertUInt64ToString(ratio, s);
888           MyStringCat(s, L"%");
889           SetItemText(IDT_PROGRESS_RATIO_VAL, s);
890         }
891       }
892     }
893   }
894 
895   if (needSetTitle || titleFileName_Changed)
896     SetTitleText();
897 
898   if (status_Changed)
899   {
900     UString s = _status;
901     ReduceString(s, _numReduceSymbols);
902     SetItemText(IDT_PROGRESS_STATUS, _status);
903   }
904 
905   if (curFilePath_Changed)
906   {
907     UString s1, s2;
908     if (_isDir)
909       s1 = _filePath;
910     else
911     {
912       int slashPos = _filePath.ReverseFind_PathSepar();
913       if (slashPos >= 0)
914       {
915         s1.SetFrom(_filePath, (unsigned)(slashPos + 1));
916         s2 = _filePath.Ptr((unsigned)(slashPos + 1));
917       }
918       else
919         s2 = _filePath;
920     }
921     ReduceString(s1, _numReduceSymbols);
922     ReduceString(s2, _numReduceSymbols);
923     s1.Add_LF();
924     s1 += s2;
925     SetItemText(IDT_PROGRESS_FILE_NAME, s1);
926   }
927 }
928 
OnTimer(WPARAM , LPARAM )929 bool CProgressDialog::OnTimer(WPARAM /* timerID */, LPARAM /* callback */)
930 {
931   if (Sync.Get_Paused())
932     return true;
933   CheckNeedClose();
934   UpdateStatInfo(false);
935   return true;
936 }
937 
938 struct CWaitCursor
939 {
940   HCURSOR _waitCursor;
941   HCURSOR _oldCursor;
CWaitCursorCWaitCursor942   CWaitCursor()
943   {
944     _waitCursor = LoadCursor(NULL, IDC_WAIT);
945     if (_waitCursor != NULL)
946       _oldCursor = SetCursor(_waitCursor);
947   }
~CWaitCursorCWaitCursor948   ~CWaitCursor()
949   {
950     if (_waitCursor != NULL)
951       SetCursor(_oldCursor);
952   }
953 };
954 
Create(const UString &title, NWindows::CThread &thread, HWND wndParent)955 INT_PTR CProgressDialog::Create(const UString &title, NWindows::CThread &thread, HWND wndParent)
956 {
957   INT_PTR res = 0;
958   try
959   {
960     if (WaitMode)
961     {
962       CWaitCursor waitCursor;
963       HANDLE h[] = { thread, _createDialogEvent };
964 
965       const DWORD res2 = WaitForMultipleObjects(Z7_ARRAY_SIZE(h), h, FALSE, kCreateDelay);
966       if (res2 == WAIT_OBJECT_0 && !Sync.ThereIsMessage())
967         return 0;
968     }
969     _title = title;
970     BIG_DIALOG_SIZE(360, 192);
971     res = CModalDialog::Create(SIZED_DIALOG(IDD_PROGRESS), wndParent);
972   }
973   catch(...)
974   {
975     _wasCreated = true;
976     _dialogCreatedEvent.Set();
977   }
978   thread.Wait_Close();
979   if (!MessagesDisplayed)
980     MessageBoxW(wndParent, L"Progress Error", L"7-Zip", MB_ICONERROR);
981   return res;
982 }
983 
OnExternalCloseMessage()984 bool CProgressDialog::OnExternalCloseMessage()
985 {
986   // it doesn't work if there is MessageBox.
987   // #ifdef __ITaskbarList3_INTERFACE_DEFINED__
988   SetTaskbarProgressState(TBPF_NOPROGRESS);
989   // #endif
990   // AddToTitle(L"Finished ");
991   // SetText(L"Finished2 ");
992 
993   UpdateStatInfo(true);
994 
995   SetItemText(IDCANCEL, LangString(IDS_CLOSE));
996   ::SendMessage(GetItem(IDCANCEL), BM_SETSTYLE, BS_DEFPUSHBUTTON, MAKELPARAM(TRUE, 0));
997   HideItem(IDB_PROGRESS_BACKGROUND);
998   HideItem(IDB_PAUSE);
999 
1000   ProcessWasFinished_GuiVirt();
1001 
1002   bool thereAreMessages;
1003   CProgressFinalMessage fm;
1004   {
1005     NSynchronization::CCriticalSectionLock lock(Sync._cs);
1006     thereAreMessages = !Sync.Messages.IsEmpty();
1007     fm = Sync.FinalMessage;
1008   }
1009 
1010   if (!fm.ErrorMessage.Message.IsEmpty())
1011   {
1012     MessagesDisplayed = true;
1013     if (fm.ErrorMessage.Title.IsEmpty())
1014       fm.ErrorMessage.Title = "7-Zip";
1015     MessageBoxW(*this, fm.ErrorMessage.Message, fm.ErrorMessage.Title, MB_ICONERROR);
1016   }
1017   else if (!thereAreMessages)
1018   {
1019     MessagesDisplayed = true;
1020 
1021     if (!fm.OkMessage.Message.IsEmpty())
1022     {
1023       if (fm.OkMessage.Title.IsEmpty())
1024         fm.OkMessage.Title = "7-Zip";
1025       MessageBoxW(*this, fm.OkMessage.Message, fm.OkMessage.Title, MB_OK);
1026     }
1027   }
1028 
1029   if (thereAreMessages && !_cancelWasPressed)
1030   {
1031     _waitCloseByCancelButton = true;
1032     UpdateMessagesDialog();
1033     return true;
1034   }
1035 
1036   End(0);
1037   return true;
1038 }
1039 
OnMessage(UINT message, WPARAM wParam, LPARAM lParam)1040 bool CProgressDialog::OnMessage(UINT message, WPARAM wParam, LPARAM lParam)
1041 {
1042   switch (message)
1043   {
1044     case kCloseMessage:
1045     {
1046       if (_timer)
1047       {
1048         /* 21.03 : KillTimer(kTimerID) instead of KillTimer(_timer).
1049            But (_timer == kTimerID) in Win10. So it worked too */
1050         KillTimer(kTimerID);
1051         _timer = 0;
1052       }
1053       if (_inCancelMessageBox)
1054       {
1055         /* if user is in MessageBox(), we will call OnExternalCloseMessage()
1056            later, when MessageBox() will be closed */
1057         _externalCloseMessageWasReceived = true;
1058         break;
1059       }
1060       return OnExternalCloseMessage();
1061     }
1062     /*
1063     case WM_SETTEXT:
1064     {
1065       if (_timer == 0)
1066         return true;
1067       break;
1068     }
1069     */
1070   }
1071   return CModalDialog::OnMessage(message, wParam, lParam);
1072 }
1073 
SetTitleText()1074 void CProgressDialog::SetTitleText()
1075 {
1076   UString s;
1077   if (Sync.Get_Paused())
1078   {
1079     s += _paused_String;
1080     s.Add_Space();
1081   }
1082   if (IS_DEFINED_VAL(_prevPercentValue))
1083   {
1084     char temp[32];
1085     ConvertUInt64ToString(_prevPercentValue, temp);
1086     s += temp;
1087     s += '%';
1088   }
1089   if (!_foreground)
1090   {
1091     s.Add_Space();
1092     s += _backgrounded_String;
1093   }
1094 
1095   s.Add_Space();
1096   #ifndef Z7_SFX
1097   {
1098     unsigned len = s.Len();
1099     s += MainAddTitle;
1100     AddToTitle(s);
1101     s.DeleteFrom(len);
1102   }
1103   #endif
1104 
1105   s += _title;
1106   if (!_titleFileName.IsEmpty())
1107   {
1108     UString fileName = _titleFileName;
1109     ReduceString(fileName, kTitleFileNameSizeLimit);
1110     s.Add_Space();
1111     s += fileName;
1112   }
1113   SetText(s);
1114 }
1115 
SetPauseText()1116 void CProgressDialog::SetPauseText()
1117 {
1118   SetItemText(IDB_PAUSE, Sync.Get_Paused() ? _continue_String : _pause_String);
1119   SetTitleText();
1120 }
1121 
OnPauseButton()1122 void CProgressDialog::OnPauseButton()
1123 {
1124   bool paused = !Sync.Get_Paused();
1125   Sync.Set_Paused(paused);
1126   UInt32 curTime = ::GetTickCount();
1127   if (paused)
1128     _elapsedTime += (curTime - _prevTime);
1129   SetTaskbarProgressState();
1130   _prevTime = curTime;
1131   SetPauseText();
1132 }
1133 
SetPriorityText()1134 void CProgressDialog::SetPriorityText()
1135 {
1136   SetItemText(IDB_PROGRESS_BACKGROUND, _foreground ?
1137       _background_String :
1138       _foreground_String);
1139   SetTitleText();
1140 }
1141 
OnPriorityButton()1142 void CProgressDialog::OnPriorityButton()
1143 {
1144   _foreground = !_foreground;
1145   #ifndef UNDER_CE
1146   SetPriorityClass(GetCurrentProcess(), _foreground ? NORMAL_PRIORITY_CLASS: IDLE_PRIORITY_CLASS);
1147   #endif
1148   SetPriorityText();
1149 }
1150 
AddMessageDirect(LPCWSTR message, bool needNumber)1151 void CProgressDialog::AddMessageDirect(LPCWSTR message, bool needNumber)
1152 {
1153   wchar_t sz[16];
1154   sz[0] = 0;
1155   if (needNumber)
1156     ConvertUInt32ToString(_numMessages + 1, sz);
1157   const unsigned itemIndex = _messageStrings.Size(); // _messageList.GetItemCount();
1158   if (_messageList.InsertItem(itemIndex, sz) == (int)itemIndex)
1159   {
1160     _messageList.SetSubItem(itemIndex, 1, message);
1161     _messageStrings.Add(message);
1162   }
1163 }
1164 
AddMessage(LPCWSTR message)1165 void CProgressDialog::AddMessage(LPCWSTR message)
1166 {
1167   UString s = message;
1168   bool needNumber = true;
1169   while (!s.IsEmpty())
1170   {
1171     const int pos = s.Find(L'\n');
1172     if (pos < 0)
1173       break;
1174     AddMessageDirect(s.Left((unsigned)pos), needNumber);
1175     needNumber = false;
1176     s.DeleteFrontal((unsigned)pos + 1);
1177   }
1178   AddMessageDirect(s, needNumber);
1179   _numMessages++;
1180 }
1181 
GetNumDigits(UInt32 val)1182 static unsigned GetNumDigits(UInt32 val)
1183 {
1184   unsigned i;
1185   for (i = 0; val >= 10; i++)
1186     val /= 10;
1187   return i;
1188 }
1189 
UpdateMessagesDialog()1190 void CProgressDialog::UpdateMessagesDialog()
1191 {
1192   UStringVector messages;
1193   {
1194     NSynchronization::CCriticalSectionLock lock(Sync._cs);
1195     unsigned num = Sync.Messages.Size();
1196     if (num > _numPostedMessages)
1197     {
1198       messages.ClearAndReserve(num - _numPostedMessages);
1199       for (unsigned i = _numPostedMessages; i < num; i++)
1200         messages.AddInReserved(Sync.Messages[i]);
1201       _numPostedMessages = num;
1202     }
1203   }
1204   if (!messages.IsEmpty())
1205   {
1206     FOR_VECTOR (i, messages)
1207       AddMessage(messages[i]);
1208     if (_numAutoSizeMessages < 256 || GetNumDigits(_numPostedMessages) > GetNumDigits(_numAutoSizeMessages))
1209     {
1210       _messageList.SetColumnWidthAuto(0);
1211       _messageList.SetColumnWidthAuto(1);
1212       _numAutoSizeMessages = _numPostedMessages;
1213     }
1214   }
1215 }
1216 
1217 
OnButtonClicked(unsigned buttonID, HWND buttonHWND)1218 bool CProgressDialog::OnButtonClicked(unsigned buttonID, HWND buttonHWND)
1219 {
1220   switch (buttonID)
1221   {
1222     // case IDOK: // if IDCANCEL is not DEFPUSHBUTTON
1223     case IDCANCEL:
1224     {
1225       if (_waitCloseByCancelButton)
1226       {
1227         MessagesDisplayed = true;
1228         End(IDCLOSE);
1229         break;
1230       }
1231 
1232       if (_cancelWasPressed)
1233         return true;
1234 
1235       const bool paused = Sync.Get_Paused();
1236 
1237       if (!paused)
1238       {
1239         OnPauseButton();
1240       }
1241 
1242       _inCancelMessageBox = true;
1243       const int res = ::MessageBoxW(*this, LangString(IDS_PROGRESS_ASK_CANCEL), _title, MB_YESNOCANCEL);
1244       _inCancelMessageBox = false;
1245       if (res == IDYES)
1246         _cancelWasPressed = true;
1247 
1248       if (!paused)
1249       {
1250         OnPauseButton();
1251       }
1252 
1253       if (_externalCloseMessageWasReceived)
1254       {
1255         /* we have received kCloseMessage while we were in MessageBoxW().
1256            so we call OnExternalCloseMessage() here.
1257            it can show MessageBox and it can close dialog */
1258         OnExternalCloseMessage();
1259         return true;
1260       }
1261 
1262       if (!_cancelWasPressed)
1263         return true;
1264 
1265       MessagesDisplayed = true;
1266       // we will call Sync.Set_Stopped(true) in OnButtonClicked() : OnCancel()
1267       break;
1268     }
1269 
1270     case IDB_PAUSE:
1271       OnPauseButton();
1272       return true;
1273     case IDB_PROGRESS_BACKGROUND:
1274       OnPriorityButton();
1275       return true;
1276   }
1277   return CModalDialog::OnButtonClicked(buttonID, buttonHWND);
1278 }
1279 
CheckNeedClose()1280 void CProgressDialog::CheckNeedClose()
1281 {
1282   if (_needClose)
1283   {
1284     PostMsg(kCloseMessage);
1285     _needClose = false;
1286   }
1287 }
1288 
ProcessWasFinished()1289 void CProgressDialog::ProcessWasFinished()
1290 {
1291   // Set Window title here.
1292   if (!WaitMode)
1293     WaitCreating();
1294 
1295   if (_wasCreated)
1296     PostMsg(kCloseMessage);
1297   else
1298     _needClose = true;
1299 }
1300 
1301 
OnNotify(UINT , LPNMHDR header)1302 bool CProgressDialog::OnNotify(UINT /* controlID */, LPNMHDR header)
1303 {
1304   if (header->hwndFrom != _messageList)
1305     return false;
1306   switch (header->code)
1307   {
1308     case LVN_KEYDOWN:
1309     {
1310       LPNMLVKEYDOWN keyDownInfo = LPNMLVKEYDOWN(header);
1311       switch (keyDownInfo->wVKey)
1312       {
1313         case 'A':
1314         {
1315           if (IsKeyDown(VK_CONTROL))
1316           {
1317             _messageList.SelectAll();
1318             return true;
1319           }
1320           break;
1321         }
1322         case VK_INSERT:
1323         case 'C':
1324         {
1325           if (IsKeyDown(VK_CONTROL))
1326           {
1327             CopyToClipboard();
1328             return true;
1329           }
1330           break;
1331         }
1332       }
1333     }
1334   }
1335   return false;
1336 }
1337 
1338 
ListView_GetSelected(NControl::CListView &listView, CUIntVector &vector)1339 static void ListView_GetSelected(NControl::CListView &listView, CUIntVector &vector)
1340 {
1341   vector.Clear();
1342   int index = -1;
1343   for (;;)
1344   {
1345     index = listView.GetNextSelectedItem(index);
1346     if (index < 0)
1347       break;
1348     vector.Add((unsigned)index);
1349   }
1350 }
1351 
1352 
CopyToClipboard()1353 void CProgressDialog::CopyToClipboard()
1354 {
1355   CUIntVector indexes;
1356   ListView_GetSelected(_messageList, indexes);
1357   UString s;
1358   unsigned numIndexes = indexes.Size();
1359   if (numIndexes == 0)
1360     numIndexes = (unsigned)_messageList.GetItemCount();
1361 
1362   for (unsigned i = 0; i < numIndexes; i++)
1363   {
1364     const unsigned index = (i < indexes.Size() ? indexes[i] : i);
1365     // s.Add_UInt32(index);
1366     // s += ": ";
1367     s += _messageStrings[index];
1368     {
1369       s +=
1370         #ifdef _WIN32
1371           "\r\n"
1372         #else
1373           "\n"
1374         #endif
1375         ;
1376     }
1377   }
1378 
1379   ClipboardSetText(*this, s);
1380 }
1381 
1382 
MyThreadFunction(void *param)1383 static THREAD_FUNC_DECL MyThreadFunction(void *param)
1384 {
1385   CProgressThreadVirt *p = (CProgressThreadVirt *)param;
1386   try
1387   {
1388     p->Process();
1389     p->ThreadFinishedOK = true;
1390   }
1391   catch (...) { p->Result = E_FAIL; }
1392   return 0;
1393 }
1394 
1395 
Create(const UString &title, HWND parentWindow)1396 HRESULT CProgressThreadVirt::Create(const UString &title, HWND parentWindow)
1397 {
1398   NWindows::CThread thread;
1399   const WRes wres = thread.Create(MyThreadFunction, this);
1400   if (wres != 0)
1401     return HRESULT_FROM_WIN32(wres);
1402   CProgressDialog::Create(title, thread, parentWindow);
1403   return S_OK;
1404 }
1405 
AddMessageToString(UString &dest, const UString &src)1406 static void AddMessageToString(UString &dest, const UString &src)
1407 {
1408   if (!src.IsEmpty())
1409   {
1410     if (!dest.IsEmpty())
1411       dest.Add_LF();
1412     dest += src;
1413   }
1414 }
1415 
Process()1416 void CProgressThreadVirt::Process()
1417 {
1418   CProgressCloser closer(*this);
1419   UString m;
1420   try { Result = ProcessVirt(); }
1421   catch(const wchar_t *s) { m = s; }
1422   catch(const UString &s) { m = s; }
1423   catch(const char *s) { m = GetUnicodeString(s); }
1424   catch(int v)
1425   {
1426     m = "Error #";
1427     m.Add_UInt32((unsigned)v);
1428   }
1429   catch(...) { m = "Error"; }
1430   if (Result != E_ABORT)
1431   {
1432     if (m.IsEmpty() && Result != S_OK)
1433       m = HResultToMessage(Result);
1434   }
1435   AddMessageToString(m, FinalMessage.ErrorMessage.Message);
1436 
1437   {
1438     FOR_VECTOR(i, ErrorPaths)
1439     {
1440       if (i >= 32)
1441         break;
1442       AddMessageToString(m, fs2us(ErrorPaths[i]));
1443     }
1444   }
1445 
1446   CProgressSync &sync = Sync;
1447   NSynchronization::CCriticalSectionLock lock(sync._cs);
1448   if (m.IsEmpty())
1449   {
1450     if (!FinalMessage.OkMessage.Message.IsEmpty())
1451       sync.FinalMessage.OkMessage = FinalMessage.OkMessage;
1452   }
1453   else
1454   {
1455     sync.FinalMessage.ErrorMessage.Message = m;
1456     if (Result == S_OK)
1457       Result = E_FAIL;
1458   }
1459 }
1460 
HResultToMessage(HRESULT errorCode)1461 UString HResultToMessage(HRESULT errorCode)
1462 {
1463   if (errorCode == E_OUTOFMEMORY)
1464     return LangString(IDS_MEM_ERROR);
1465   else
1466     return NError::MyFormatMessage(errorCode);
1467 }
1468