|
1
|
/*++ |
|
2
|
|
|
3
|
Copyright (c) Microsoft. All rights reserved. |
|
4
|
|
|
5
|
Module Name: |
|
6
|
|
|
7
|
ConsoleProgressBar.cpp |
|
8
|
|
|
9
|
Abstract: |
|
10
|
|
|
11
|
This file contains the ConsoleProgressBar implementation |
|
12
|
|
|
13
|
--*/ |
|
14
|
|
|
15
|
#include "precomp.h" |
|
16
|
#include "ConsoleProgressBar.h" |
|
17
|
|
|
18
|
constexpr size_t c_progressBarWidth = 58; |
|
19
|
constexpr size_t c_lineWidth = c_progressBarWidth + 2; |
|
20
|
constexpr size_t c_progressBarBufferSize = c_lineWidth + 1; |
|
21
|
constexpr LPCWSTR c_progressBarFormatString = L"[%58s]"; |
|
22
|
constexpr LPCWSTR c_clearFormatString = L"%60s"; |
|
23
|
|
|
24
|
wsl::windows::common::ConsoleProgressBar::ConsoleProgressBar() |
|
25
|
{ |
|
26
|
m_outputHandle = GetStdHandle(STD_ERROR_HANDLE); |
|
27
|
m_isOutputConsole = HandleIsConsole(m_outputHandle); |
|
28
|
m_progressString = wil::make_hlocal_string_nothrow(nullptr, c_lineWidth); |
|
29
|
THROW_IF_NULL_ALLOC(m_progressString); |
|
30
|
} |
|
31
|
|
|
32
|
// Print |
|
33
|
// |
|
34
|
// formats and prints the progress bar to the console with the given progress indicated |
|
35
|
HRESULT |
|
36
|
wsl::windows::common::ConsoleProgressBar::Print(_In_ uint64_t progress, _In_ uint64_t total) |
|
37
|
{ |
|
38
|
if (!m_isOutputConsole) |
|
39
|
{ |
|
40
|
return S_FALSE; |
|
41
|
} |
|
42
|
|
|
43
|
total = std::max<uint64_t>(total, 1); |
|
44
|
progress = std::min<uint64_t>(progress, total); |
|
45
|
if ((progress == m_previousProgress) && (total == m_previousTotal)) |
|
46
|
{ |
|
47
|
return S_OK; |
|
48
|
} |
|
49
|
|
|
50
|
const float percent = progress / static_cast<float>(total); |
|
51
|
const size_t filledBarCount = static_cast<size_t>(c_progressBarWidth * percent); |
|
52
|
RETURN_IF_FAILED(StringCchPrintfW(m_progressString.get(), c_progressBarBufferSize, c_progressBarFormatString, L"")); |
|
53
|
|
|
54
|
wil::unique_hlocal_string percentString; |
|
55
|
RETURN_IF_FAILED(wil::str_printf_nothrow(percentString, L"%2.1f%%", percent * 100.0f)); |
|
56
|
|
|
57
|
RETURN_HR_IF(E_INVALIDARG, (wcslen(percentString.get()) > wcslen(m_progressString.get()))); |
|
58
|
|
|
59
|
// Write the 'filled' progress symbol '=' into the bar |
|
60
|
for (size_t i = 0; i < filledBarCount; i++) |
|
61
|
{ |
|
62
|
m_progressString.get()[i + 1] = L'='; |
|
63
|
} |
|
64
|
|
|
65
|
// Insert the percentage-formatted string into the middle of the progress bar |
|
66
|
size_t percentOffset = (c_lineWidth - wcslen(percentString.get())) / 2; |
|
67
|
for (LPWSTR currentPercentCharacter = percentString.get(); L'\0' != *currentPercentCharacter; currentPercentCharacter++) |
|
68
|
{ |
|
69
|
m_progressString.get()[percentOffset++] = *currentPercentCharacter; |
|
70
|
} |
|
71
|
|
|
72
|
RETURN_IF_FAILED(PrintAndResetPosition(m_progressString.get())); |
|
73
|
|
|
74
|
m_previousProgress = progress; |
|
75
|
m_previousTotal = total; |
|
76
|
return S_OK; |
|
77
|
} |
|
78
|
|
|
79
|
// Clear |
|
80
|
// |
|
81
|
// removes the progress bar from the console |
|
82
|
HRESULT |
|
83
|
wsl::windows::common::ConsoleProgressBar::Clear() |
|
84
|
{ |
|
85
|
if (!m_isOutputConsole) |
|
86
|
{ |
|
87
|
return S_FALSE; |
|
88
|
} |
|
89
|
|
|
90
|
RETURN_IF_FAILED(StringCchPrintfW(m_progressString.get(), c_progressBarBufferSize, c_clearFormatString, L"")); |
|
91
|
return PrintAndResetPosition(m_progressString.get()); |
|
92
|
} |
|
93
|
|
|
94
|
// PrintAndResetPosition |
|
95
|
// |
|
96
|
// This function writes a given wide character string to the given output handle |
|
97
|
// and moves the cursor back to the start position |
|
98
|
HRESULT |
|
99
|
wsl::windows::common::ConsoleProgressBar::PrintAndResetPosition(_In_ LPCWSTR string) const |
|
100
|
{ |
|
101
|
RETURN_HR_IF_NULL(E_INVALIDARG, string); |
|
102
|
|
|
103
|
CONSOLE_SCREEN_BUFFER_INFO consoleBufferInfo; |
|
104
|
RETURN_LAST_ERROR_IF(!GetConsoleScreenBufferInfo(m_outputHandle, &consoleBufferInfo)); |
|
105
|
|
|
106
|
DWORD stringLength = static_cast<DWORD>(wcslen(string)); |
|
107
|
RETURN_LAST_ERROR_IF(!WriteConsoleW(m_outputHandle, string, stringLength, &stringLength, nullptr)); |
|
108
|
|
|
109
|
RETURN_LAST_ERROR_IF(!SetConsoleCursorPosition(m_outputHandle, consoleBufferInfo.dwCursorPosition)); |
|
110
|
return S_OK; |
|
111
|
} |
|
112
|
|
|
113
|
// HandleIsConsole |
|
114
|
// |
|
115
|
// Determines if the given handle is a console |
|
116
|
bool wsl::windows::common::ConsoleProgressBar::HandleIsConsole(_In_ HANDLE handle) |
|
117
|
{ |
|
118
|
DWORD mode; |
|
119
|
return (GetFileType(handle) == FILE_TYPE_CHAR) && GetConsoleMode(handle, &mode); |
|
120
|
} |