master
cpp 120 lines 3.72 KB
Raw
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 }