microsoft / microsoft/terminal

conhost: SetConsoleWindowInfo broken when used in alternate screen buffer

Open
#13,741 7 comments 0 reactions 0 assignees View on GitHub
Area-Server Issue-Bug Product-Conhost
Dominant language
C++
Stars
105k
Forks
9.6k
Avg merge
3d 17h
Merged PRs (30d)
29

Description

### Windows Terminal version

_No response_

### Windows build number

10.0.19041.1806

### Other Software

conhost.exe 10.0.19041.1566
conhostV1.dll 10.0.19041.21
Visual Studio 2019 version 16.11.18

### Steps to reproduce

Use this test program:

```
#include
#include

bool get_info_and_print(HANDLE h_stdout, CONSOLE_SCREEN_BUFFER_INFO& buf_info)
{
if (!GetConsoleScreenBufferInfo(h_stdout, &buf_info)) {
std::cerr << "Failed to get screen buffer info.\n";
return false;
}

std::cout << "Screen buffer size: "
<< buf_info.dwSize.X << "x" << buf_info.dwSize.Y << "\n";
std::cout << "Window size: "
<< buf_info.srWindow.Right + 1 - buf_info.srWindow.Left << "x"
<< buf_info.srWindow.Bottom + 1 - buf_info.srWindow.Top << "\n\n";
std::cin.get();
return true;
}

// Set screen buffer size, then set the window size to maximum.
// Revert back to original window size and buffer size afterward.
// Assume initial console size is smaller than 150x45, larger than 10x10
bool do_test()
{
constexpr COORD target_buffer_size{ 150, 45 };

HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
if (h_stdout == INVALID_HANDLE_VALUE) return 1;

std::cout << "Orignial buffer & window\n";

// Get original screen buffer info
CONSOLE_SCREEN_BUFFER_INFO original_buf_info;
if (!get_info_and_print(h_stdout, original_buf_info)) return false;

// Set the screen buffer to target size
if (!SetConsoleScreenBufferSize(h_stdout, target_buffer_size)) {
std::cerr << "Failed to set buffer size.\n";
}

std::cout << "Made buffer larger\n";

// Get buffer info after adjustment
CONSOLE_SCREEN_BUFFER_INFO larger_buf_info;
if (!get_info_and_print(h_stdout, larger_buf_info)) return false;

std::cout << "GetConsoleScreenBufferInfo gives dwMaximumWindowSize as "
<< larger_buf_info.dwMaximumWindowSize.X << "x"
<< larger_buf_info.dwMaximumWindowSize.Y << "!\n";

// Attempt to set window size to maximum allowed.
SMALL_RECT larger_window_size{
0, 0,
larger_buf_info.dwMaximumWindowSize.X - 1,
larger_buf_info.dwMaximumWindowSize.Y - 1
};

// ################################################################
// THIS FAILS IN ALT BUFFER WITH ERROR_INVALID_PARAMETER DESPITE
// ADHERING TO THE PARAMETER LIMITS GIVEN BY ConsoleScreenBufferInfo
if (!SetConsoleWindowInfo(h_stdout, TRUE, &larger_window_size)) {
DWORD err_code = GetLastError();
std::cerr << "Failed to set window size. Err code: " << err_code << "\n";
}

std::cout << "Also made window larger\n";
// Get buffer info after adjustment
CONSOLE_SCREEN_BUFFER_INFO larger_buf_info_temp;
if (!get_info_and_print(h_stdout, larger_buf_info_temp)) return false;

// Revert window
if (!SetConsoleWindowInfo(h_stdout, TRUE, &original_buf_info.srWindow)) {
std::cerr << "Failed to revert window size\n";
}

if (!SetConsoleScreenBufferSize(h_stdout, original_buf_info.dwSize)) {
std::cerr << "Failed to revert screen buffer size\n";
}

// Attempt to make the window size smaller than the original size
SMALL_RECT smaller_window_size {
original_buf_info.srWindow.Left,
original_buf_info.srWindow.Top,
original_buf_info.srWindow.Right - 10,
original_buf_info.srWindow.Bottom - 10
};

// ################################################################
// THIS FAILS IN ALT BUFFER BUT RETURNS NONZERO (SUCCESS)
if (!SetConsoleWindowInfo(h_stdout, TRUE, &smaller_window_size)) {
DWORD err_code = GetLastError();
std::cerr << "Failed to set window size. Err code: " << err_code << "\n";
}

std::cout << "Made window smaller\n";
// Get buffer info after adjustment
CONSOLE_SCREEN_BUFFER_INFO smaller_buf_info_temp;
if (!get_info_and_print(h_stdout, smaller_buf_info_temp)) return false;

// Revert window
if (!SetConsoleWindowInfo(h_stdout, TRUE, &original_buf_info.srWindow)) {
std::cerr << "Failed to revert window size\n";
}

return true;
}

int main()
{
// Attempt to enable virtual terminal prcessing
HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
if (h_stdout == INVALID_HANDLE_VALUE) return 1;

DWORD stdout_mode;
if (!GetConsoleMode(h_stdout, &stdout_mode)) return 1;

if (!SetConsoleMode(h_stdout, stdout_mode |
ENABLE_VIRTUAL_TERMINAL_PROCESSING |
ENABLE_PROCESSED_OUTPUT))
{
return 1;
}

std::cout << "Testing on main screen buffer.\n";
do_test();

// Switch to alternate screen buffer
std::cout << "\x1b[?1049h" << std::flush;

std::cout << "Testing on alternate screen buffer.\n";
do_test();

// Switch back to main screen buffer
std::cout << "\x1b[?1049l" << std::flush;

// Restore console mode
SetConsoleMode(h_stdout, stdout_mode);

return 0;
}
```

### Expected Behavior

_No response_

### Actual Behavior

Recording compiled with Visual Studio 2019 version 16.11.18, default console project with Debug/x86 target.

https://user-images.githubusercontent.com/7597319/184560547-bc2d3926-633a-4c9c-bdf6-15a4e9aac47d.mp4

1. When trying to increase the window size to the maximum size allowed according to `GetConsoleScreenBufferInfo`, `SetConsoleWindowInfo` returns 0 with the invalid parameters error code.
2. When trying to decrease the window size, `SetConsoleWindowInfo` returns non-zero indicating success, but the actual window size remains unchanged.

I don't know if resizing is ever supposed to work in the alternate screen buffer, but even if it's supposed to fail, the way it fails seems pretty nonsensical to me.

Contributor guide

Open the contributing guide

Research direction

Start by compiling and running the provided C++ reproduction on the specified Windows build, comparing SetConsoleWindowInfo behavior in the main and alternate screen buffers. Trace the conhost handling of alternate-buffer window resizing and determine the intended behavior for maximum and smaller window requests. Done means the behavior is corrected or explicitly defined and covered by an appropriate regression test.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
cli, operating-systems
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
28/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.