Skip to content

Commit cc603bd

Browse files
author
Ben Hillis
committed
cleanup: refactor svccommio class
1 parent 49864b5 commit cc603bd

File tree

2 files changed

+147
-225
lines changed

2 files changed

+147
-225
lines changed

src/windows/common/svccommio.cpp

Lines changed: 107 additions & 207 deletions
Original file line numberDiff line numberDiff line change
@@ -18,265 +18,165 @@ Module Name:
1818
#pragma hdrstop
1919

2020
namespace {
21-
void ChangeConsoleMode(_In_ HANDLE File, _In_ DWORD ConsoleMode)
22-
{
23-
//
24-
// Use the invalid parameter error code to detect the v1 console that does
25-
// not support the provided mode. This can be improved in the future when
26-
// a more elegant solution exists.
27-
//
28-
// N.B. Ignore failures setting the mode if the console has already
29-
// disconnected.
30-
//
31-
32-
if (!SetConsoleMode(File, ConsoleMode))
33-
{
34-
switch (GetLastError())
35-
{
36-
case ERROR_INVALID_PARAMETER:
37-
THROW_HR(WSL_E_CONSOLE);
38-
39-
case ERROR_PIPE_NOT_CONNECTED:
40-
break;
4121

42-
default:
43-
THROW_LAST_ERROR();
44-
}
45-
}
22+
bool IsConsoleHandle(_In_ HANDLE Handle)
23+
{
24+
DWORD Mode;
25+
return GetFileType(Handle) == FILE_TYPE_CHAR && GetConsoleMode(Handle, &Mode);
4626
}
4727

48-
void ConfigureStdHandles(_Inout_ LXSS_STD_HANDLES_INFO& StdHandlesInfo)
28+
void TrySetConsoleMode(_In_ HANDLE Handle, _In_ DWORD Mode)
4929
{
50-
//
51-
// Check stdin to see if it is a console or another device. If it is
52-
// a console, configure it to raw processing mode and VT-100 support. If the
53-
// force console I/O is requested, ignore stdin and get active console input
54-
// handle instead.
55-
//
56-
57-
UINT NewConsoleInputCP = 0;
58-
DWORD NewConsoleInputMode = 0;
59-
BOOLEAN IsConsoleInput = StdHandlesInfo.IsConsoleInput;
60-
BOOLEAN IsConsoleOutput = StdHandlesInfo.IsConsoleOutput;
61-
BOOLEAN IsConsoleError = StdHandlesInfo.IsConsoleError;
62-
DWORD SavedInputMode = StdHandlesInfo.SavedInputMode;
63-
DWORD SavedOutputMode = StdHandlesInfo.SavedOutputMode;
64-
UINT SavedInputCP = StdHandlesInfo.SavedInputCP;
65-
UINT SavedOutputCP = StdHandlesInfo.SavedOutputCP;
66-
CONSOLE_SCREEN_BUFFER_INFO ScreenBufferInfo;
67-
auto RestoreInputHandle = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&] {
68-
if (NewConsoleInputCP != 0)
69-
{
70-
SetConsoleCP(SavedInputCP);
71-
}
72-
73-
if (NewConsoleInputMode != 0)
74-
{
75-
ChangeConsoleMode(StdHandlesInfo.InputHandle, SavedInputMode);
76-
}
77-
});
78-
79-
IsConsoleInput = FALSE;
80-
if ((GetFileType(StdHandlesInfo.InputHandle) == FILE_TYPE_CHAR) && (GetConsoleMode(StdHandlesInfo.InputHandle, &SavedInputMode)))
30+
if (!SetConsoleMode(Handle, Mode))
8131
{
82-
IsConsoleInput = TRUE;
83-
NewConsoleInputMode = SavedInputMode;
84-
WI_SetAllFlags(NewConsoleInputMode, (ENABLE_WINDOW_INPUT | ENABLE_VIRTUAL_TERMINAL_INPUT));
85-
WI_ClearAllFlags(NewConsoleInputMode, (ENABLE_ECHO_INPUT | ENABLE_INSERT_MODE | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT));
86-
ChangeConsoleMode(StdHandlesInfo.InputHandle, NewConsoleInputMode);
87-
88-
//
89-
// Set the console input to the UTF-8 code page.
90-
//
91-
92-
SavedInputCP = GetConsoleCP();
93-
NewConsoleInputCP = CP_UTF8;
94-
THROW_LAST_ERROR_IF(!::SetConsoleCP(NewConsoleInputCP));
95-
}
96-
97-
bool RestoreMode = false;
98-
bool RestoreCp = false;
99-
auto RestoreOutput = wil::scope_exit([&] {
100-
if (RestoreMode)
32+
const auto Error = GetLastError();
33+
if (Error != ERROR_INVALID_PARAMETER && Error != ERROR_PIPE_NOT_CONNECTED)
10134
{
102-
SetConsoleMode(StdHandlesInfo.ConsoleOutputHandle.get(), SavedOutputMode);
35+
LOG_IF_WIN32_ERROR(Error);
10336
}
37+
}
38+
}
10439

105-
if (RestoreCp)
106-
{
107-
SetConsoleOutputCP(SavedOutputCP);
108-
}
109-
});
40+
} // namespace
11041

111-
//
112-
// If there is a console output handle, save the output mode and codepage so
113-
// it can be restored.
114-
//
42+
namespace wsl::windows::common {
11543

116-
if (StdHandlesInfo.ConsoleOutputHandle)
44+
// ConsoleInput implementation
45+
std::unique_ptr<ConsoleInput> ConsoleInput::Create(HANDLE Handle)
46+
{
47+
DWORD Mode;
48+
if (GetFileType(Handle) == FILE_TYPE_CHAR && GetConsoleMode(Handle, &Mode))
11749
{
118-
THROW_LAST_ERROR_IF(!::GetConsoleMode(StdHandlesInfo.ConsoleOutputHandle.get(), &SavedOutputMode));
119-
120-
//
121-
// Temporarily try both with and without the custom flag to disable newline
122-
// auto return.
123-
//
124-
125-
DWORD NewConsoleOutputMode = SavedOutputMode | ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN;
126-
if (SetConsoleMode(StdHandlesInfo.ConsoleOutputHandle.get(), NewConsoleOutputMode) == FALSE)
127-
{
128-
WI_ClearFlag(NewConsoleOutputMode, DISABLE_NEWLINE_AUTO_RETURN);
129-
ChangeConsoleMode(StdHandlesInfo.ConsoleOutputHandle.get(), NewConsoleOutputMode);
130-
}
131-
132-
RestoreMode = true;
133-
134-
//
135-
// Set the console output to the UTF-8 code page.
136-
//
50+
return std::unique_ptr<ConsoleInput>(new ConsoleInput(Handle, Mode));
51+
}
13752

138-
SavedOutputCP = GetConsoleOutputCP();
139-
THROW_LAST_ERROR_IF(!::SetConsoleOutputCP(CP_UTF8));
53+
return nullptr;
54+
}
14055

141-
RestoreCp = true;
142-
}
56+
ConsoleInput::ConsoleInput(HANDLE Handle, DWORD SavedMode) : m_Handle(Handle), m_SavedMode(SavedMode)
57+
{
58+
// Save code page
59+
m_SavedCodePage = GetConsoleCP();
14360

144-
//
145-
// If the force console I/O is requested, ignore stdout and treat the
146-
// console as the output handle.
147-
//
61+
// Configure for raw input with VT support
62+
DWORD NewMode = m_SavedMode;
63+
WI_SetAllFlags(NewMode, ENABLE_WINDOW_INPUT | ENABLE_VIRTUAL_TERMINAL_INPUT);
64+
WI_ClearAllFlags(NewMode, ENABLE_ECHO_INPUT | ENABLE_INSERT_MODE | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT);
65+
TrySetConsoleMode(Handle, NewMode);
14866

149-
IsConsoleOutput = FALSE;
150-
if ((GetFileType(StdHandlesInfo.OutputHandle) == FILE_TYPE_CHAR) &&
151-
(GetConsoleScreenBufferInfo(StdHandlesInfo.OutputHandle, &ScreenBufferInfo)))
152-
{
153-
IsConsoleOutput = TRUE;
154-
}
67+
// Set UTF-8 code page
68+
SetConsoleCP(CP_UTF8);
69+
}
15570

156-
IsConsoleError = FALSE;
157-
if ((GetFileType(StdHandlesInfo.ErrorHandle) == FILE_TYPE_CHAR) &&
158-
(GetConsoleScreenBufferInfo(StdHandlesInfo.ErrorHandle, &ScreenBufferInfo)))
71+
ConsoleInput::~ConsoleInput()
72+
{
73+
if (m_Handle)
15974
{
160-
IsConsoleError = TRUE;
75+
TrySetConsoleMode(m_Handle, m_SavedMode);
76+
SetConsoleCP(m_SavedCodePage);
16177
}
162-
163-
RestoreInputHandle.release();
164-
RestoreOutput.release();
165-
StdHandlesInfo.IsConsoleInput = IsConsoleInput;
166-
StdHandlesInfo.IsConsoleOutput = IsConsoleOutput;
167-
StdHandlesInfo.IsConsoleError = IsConsoleError;
168-
StdHandlesInfo.SavedInputMode = SavedInputMode;
169-
StdHandlesInfo.SavedOutputMode = SavedOutputMode;
170-
StdHandlesInfo.SavedInputCP = SavedInputCP;
171-
StdHandlesInfo.SavedOutputCP = SavedOutputCP;
17278
}
173-
} // namespace
17479

175-
wsl::windows::common::SvcCommIo::SvcCommIo()
80+
// ConsoleOutput implementation
81+
std::unique_ptr<ConsoleOutput> ConsoleOutput::Create()
17682
{
177-
_stdHandlesInfo.InputHandle = GetStdHandle(STD_INPUT_HANDLE);
178-
_stdHandlesInfo.OutputHandle = GetStdHandle(STD_OUTPUT_HANDLE);
179-
_stdHandlesInfo.ErrorHandle = GetStdHandle(STD_ERROR_HANDLE);
180-
_stdHandlesInfo.ConsoleOutputHandle.reset(
83+
wil::unique_hfile ConsoleHandle(
18184
CreateFileW(L"CONOUT$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr));
18285

183-
ConfigureStdHandles(_stdHandlesInfo);
184-
_stdHandles.StdIn.HandleType = LxssHandleInput;
185-
_stdHandles.StdIn.Handle = HandleToUlong(_stdHandlesInfo.InputHandle);
186-
_stdHandles.StdOut.HandleType = LxssHandleOutput;
187-
_stdHandles.StdOut.Handle = HandleToUlong(_stdHandlesInfo.OutputHandle);
188-
_stdHandles.StdErr.HandleType = LxssHandleOutput;
189-
_stdHandles.StdErr.Handle = HandleToUlong(_stdHandlesInfo.ErrorHandle);
190-
191-
//
192-
// N.B.: The console handle is not supposed to be closed, it is just copied
193-
// from PEB.
194-
//
195-
196-
if (_stdHandlesInfo.IsConsoleInput)
86+
if (!ConsoleHandle)
19787
{
198-
_stdHandles.StdIn.Handle = LXSS_HANDLE_USE_CONSOLE;
199-
_stdHandles.StdIn.HandleType = LxssHandleConsole;
88+
return nullptr;
20089
}
20190

202-
if (_stdHandlesInfo.IsConsoleOutput)
91+
DWORD Mode;
92+
if (GetConsoleMode(ConsoleHandle.get(), &Mode))
20393
{
204-
_stdHandles.StdOut.Handle = LXSS_HANDLE_USE_CONSOLE;
205-
_stdHandles.StdOut.HandleType = LxssHandleConsole;
94+
return std::unique_ptr<ConsoleOutput>(new ConsoleOutput(std::move(ConsoleHandle), Mode));
20695
}
20796

208-
if (_stdHandlesInfo.IsConsoleError)
209-
{
210-
_stdHandles.StdErr.Handle = LXSS_HANDLE_USE_CONSOLE;
211-
_stdHandles.StdErr.HandleType = LxssHandleConsole;
212-
}
97+
return nullptr;
21398
}
21499

215-
wsl::windows::common::SvcCommIo::~SvcCommIo()
100+
ConsoleOutput::ConsoleOutput(wil::unique_hfile ConsoleHandle, DWORD SavedMode) :
101+
m_ConsoleHandle(std::move(ConsoleHandle)), m_SavedMode(SavedMode)
216102
{
217-
try
103+
// Save code page
104+
m_SavedCodePage = GetConsoleOutputCP();
105+
106+
// Configure for VT output with DISABLE_NEWLINE_AUTO_RETURN
107+
DWORD NewMode = m_SavedMode;
108+
WI_SetAllFlags(NewMode, ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN);
109+
110+
// Try with DISABLE_NEWLINE_AUTO_RETURN first, fall back without it if not supported
111+
if (!SetConsoleMode(m_ConsoleHandle.get(), NewMode))
218112
{
219-
RestoreConsoleMode();
113+
WI_ClearFlag(NewMode, DISABLE_NEWLINE_AUTO_RETURN);
114+
TrySetConsoleMode(m_ConsoleHandle.get(), NewMode);
220115
}
221-
CATCH_LOG()
222-
}
223116

224-
PLXSS_STD_HANDLES
225-
wsl::windows::common::SvcCommIo::GetStdHandles()
226-
{
227-
return &_stdHandles;
117+
// Set UTF-8 code page
118+
SetConsoleOutputCP(CP_UTF8);
228119
}
229120

230-
COORD
231-
wsl::windows::common::SvcCommIo::GetWindowSize() const
121+
ConsoleOutput::~ConsoleOutput()
232122
{
233-
CONSOLE_SCREEN_BUFFER_INFOEX Info{};
234-
Info.cbSize = sizeof(Info);
235-
if (_stdHandlesInfo.IsConsoleOutput)
123+
if (m_ConsoleHandle)
236124
{
237-
THROW_IF_WIN32_BOOL_FALSE(::GetConsoleScreenBufferInfoEx(_stdHandlesInfo.OutputHandle, &Info));
125+
TrySetConsoleMode(m_ConsoleHandle.get(), m_SavedMode);
126+
SetConsoleOutputCP(m_SavedCodePage);
238127
}
239-
else if (_stdHandlesInfo.IsConsoleError)
240-
{
241-
THROW_IF_WIN32_BOOL_FALSE(::GetConsoleScreenBufferInfoEx(_stdHandlesInfo.ErrorHandle, &Info));
242-
}
243-
244-
return {
245-
static_cast<short>(Info.srWindow.Right - Info.srWindow.Left + 1), static_cast<short>(Info.srWindow.Bottom - Info.srWindow.Top + 1)};
246128
}
247129

248-
void wsl::windows::common::SvcCommIo::RestoreConsoleMode() const
249-
250-
/*++
251-
252-
Routine Description:
130+
// SvcCommIo implementation
131+
SvcCommIo::SvcCommIo()
132+
{
133+
const HANDLE InputHandle = GetStdHandle(STD_INPUT_HANDLE);
134+
const HANDLE OutputHandle = GetStdHandle(STD_OUTPUT_HANDLE);
135+
const HANDLE ErrorHandle = GetStdHandle(STD_ERROR_HANDLE);
253136

254-
Restores the saved input/output console mode.
137+
// Configure input console
138+
m_ConsoleInput = ConsoleInput::Create(InputHandle);
255139

256-
Arguments:
140+
// Configure output console
141+
m_ConsoleOutput = ConsoleOutput::Create();
257142

258-
None.
143+
// Initialize the standard handles structure
144+
const bool IsConsoleInput = m_ConsoleInput != nullptr;
145+
m_StdHandles.StdIn.HandleType = IsConsoleInput ? LxssHandleConsole : LxssHandleInput;
146+
m_StdHandles.StdIn.Handle = IsConsoleInput ? LXSS_HANDLE_USE_CONSOLE : HandleToUlong(InputHandle);
259147

260-
Return Value:
148+
const bool IsConsoleOutput = IsConsoleHandle(OutputHandle);
149+
m_StdHandles.StdOut.HandleType = IsConsoleOutput ? LxssHandleConsole : LxssHandleOutput;
150+
m_StdHandles.StdOut.Handle = IsConsoleOutput ? LXSS_HANDLE_USE_CONSOLE : HandleToUlong(OutputHandle);
261151

262-
None.
152+
const bool IsConsoleError = IsConsoleHandle(ErrorHandle);
153+
m_StdHandles.StdErr.HandleType = IsConsoleError ? LxssHandleConsole : LxssHandleOutput;
154+
m_StdHandles.StdErr.Handle = IsConsoleError ? LXSS_HANDLE_USE_CONSOLE : HandleToUlong(ErrorHandle);
263155

264-
--*/
156+
// Cache a console handle for GetWindowSize
157+
m_WindowSizeHandle = IsConsoleOutput ? OutputHandle : (IsConsoleError ? ErrorHandle : nullptr);
158+
}
265159

160+
PLXSS_STD_HANDLES
161+
SvcCommIo::GetStdHandles()
266162
{
267-
//
268-
// Restore the console input and output modes.
269-
//
163+
return &m_StdHandles;
164+
}
270165

271-
if (_stdHandlesInfo.ConsoleOutputHandle)
166+
COORD
167+
SvcCommIo::GetWindowSize() const
168+
{
169+
if (m_WindowSizeHandle)
272170
{
273-
ChangeConsoleMode(_stdHandlesInfo.ConsoleOutputHandle.get(), _stdHandlesInfo.SavedOutputMode);
274-
SetConsoleOutputCP(_stdHandlesInfo.SavedOutputCP);
171+
CONSOLE_SCREEN_BUFFER_INFOEX Info{};
172+
Info.cbSize = sizeof(Info);
173+
THROW_IF_WIN32_BOOL_FALSE(GetConsoleScreenBufferInfoEx(m_WindowSizeHandle, &Info));
174+
return {
175+
static_cast<short>(Info.srWindow.Right - Info.srWindow.Left + 1),
176+
static_cast<short>(Info.srWindow.Bottom - Info.srWindow.Top + 1)};
275177
}
276178

277-
if (_stdHandlesInfo.IsConsoleInput != FALSE)
278-
{
279-
ChangeConsoleMode(_stdHandlesInfo.InputHandle, _stdHandlesInfo.SavedInputMode);
280-
SetConsoleCP(_stdHandlesInfo.SavedInputCP);
281-
}
179+
return {80, 24}; // Default size if no console
282180
}
181+
182+
} // namespace wsl::windows::common

0 commit comments

Comments
 (0)