conhost: SetConsoleWindowInfo broken when used in alternate screen buffer #18180

Open
opened 2026-01-31 06:06:05 +00:00 by claunia · 7 comments
Owner

Originally created by @Gyross on GitHub (Aug 15, 2022).

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 <windows.h>
#include <iostream>

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.

Originally created by @Gyross on GitHub (Aug 15, 2022). ### 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 <windows.h> #include <iostream> 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.
claunia added the Product-ConhostIssue-BugArea-Server labels 2026-01-31 06:06:05 +00:00
Author
Owner

@Gyross commented on GitHub (Aug 16, 2022):

As an aside, can you explain the behaviour of the scrollbars?

  • When in main buffer, there are no scrollbars when window size == buffer size, and there are both horizontal and vertical scrollbars when window size X < buffer size X && window size Y < buffer size Y.
  • When in alt buffer, there is a disabled vertical scrollbar when window size == buffer size, and there is an enabled vertical scrollbar, but no horizontal scrollbar when window size X < buffer size X && window size Y < buffer size Y.

Is all this just conhost only being able to halfway enforce the buffer size == window size rule for alt buffer?

@Gyross commented on GitHub (Aug 16, 2022): As an aside, can you explain the behaviour of the scrollbars? - When in main buffer, there are no scrollbars when window size == buffer size, and there are both horizontal and vertical scrollbars when window size X < buffer size X && window size Y < buffer size Y. - When in alt buffer, there is a disabled vertical scrollbar when window size == buffer size, and there is an enabled vertical scrollbar, but no horizontal scrollbar when window size X < buffer size X && window size Y < buffer size Y. Is all this just conhost only being able to halfway enforce the buffer size == window size rule for alt buffer?
Author
Owner

@BDisp commented on GitHub (Aug 8, 2023):

The limitation of Windows Terminal being ignoring the SetConsoleScreenBufferSize to resize the buffer to the windows size is killing me. Forces the WT showing the vertical scroll bar when we don't want it. Please can someone explains why we can't resize the buffer to the same size of the visible window, when our app handles the available space? Due the others tabs? Each tab must have is own buffer size and not depends of the WT window size.

@BDisp commented on GitHub (Aug 8, 2023): The limitation of Windows Terminal being ignoring the `SetConsoleScreenBufferSize` to resize the buffer to the windows size is killing me. Forces the WT showing the vertical scroll bar when we don't want it. Please can someone explains why we can't resize the buffer to the same size of the visible window, when our app handles the available space? Due the others tabs? Each tab must have is own buffer size and not depends of the WT window size.
Author
Owner

@zadjii-msft commented on GitHub (Aug 9, 2023):

What you're describing sounds more like #5094. This is specifically a bug in resizing the alt buffer with SetConsoleWindowInfo in conhost.

er, wait though, is it? It sounds like you're trying to use SetConsoleScreenBufferSize to get rid of the scrollback, in terminal? Why wouldn't you just use the alt buffer then? (this may warrant a new discussion thread)

@zadjii-msft commented on GitHub (Aug 9, 2023): ~What you're describing sounds more like #5094~. This is specifically a bug in resizing the alt buffer with `SetConsoleWindowInfo` in _conhost_. er, wait though, is it? It sounds like you're trying to use `SetConsoleScreenBufferSize` to get rid of the scrollback, in _terminal_? Why wouldn't you just use the alt buffer then? (this may warrant a new discussion thread)
Author
Owner

@BDisp commented on GitHub (Aug 9, 2023):

What you're describing sounds more like #5094. This is specifically a bug in resizing the alt buffer with SetConsoleWindowInfo in conhost.

@zadjii-msft thanks for your feedback. Yes you are right it's more like #5094 and you'll see some comments I posted there. I don't have any issue by resizing the windows and the buffer with the same size with conhost. The trick is first resizing the buffer with the SetConsoleScreenBufferInfoEx(ScreenBuffer, ref csbi) method and then resizing the window with the SetConsoleWindowInfo(OutputHandle, true, ref winRect) method. Finally I call the SetConsoleScreenBufferInfoEx (ScreenBuffer, ref csbi) again. This only work because I use two separate handles, one for the window size (OutputHandle) and other for the buffer size (ScreenBuffer). If we use the same handle for both it will throw an exception, of course, because conhost uses the same handle.

er, wait though, is it? It sounds like you're trying to use SetConsoleScreenBufferSize to get rid of the scrollback, in terminal? Why wouldn't you just use the alt buffer then? (this may warrant a new discussion thread)

I only want to get rid the scrollback while my app is running but when I exit I restore the saved original back. What do you mean by using the alt buffer?
Resizing the buffer with Win API console app using conhost, cmd, powershell, conemu works well, but not on Windows Terminal. The only workaround that works on WT is using "\x1b[3J" escape sequence, but it shouldn't being necessary.

@BDisp commented on GitHub (Aug 9, 2023): > ~What you're describing sounds more like #5094~. This is specifically a bug in resizing the alt buffer with `SetConsoleWindowInfo` in _conhost_. > @zadjii-msft thanks for your feedback. Yes you are right it's more like #5094 and you'll see some comments I posted there. I don't have any issue by resizing the windows and the buffer with the same size with `conhost`. The trick is first resizing the buffer with the `SetConsoleScreenBufferInfoEx(ScreenBuffer, ref csbi)` method and then resizing the window with the `SetConsoleWindowInfo(OutputHandle, true, ref winRect)` method. Finally I call the `SetConsoleScreenBufferInfoEx (ScreenBuffer, ref csbi)` again. This only work because I use two separate handles, one for the window size (OutputHandle) and other for the buffer size (ScreenBuffer). If we use the same handle for both it will throw an exception, of course, because `conhost` uses the same handle. > er, wait though, is it? It sounds like you're trying to use `SetConsoleScreenBufferSize` to get rid of the scrollback, in _terminal_? Why wouldn't you just use the alt buffer then? (this may warrant a new discussion thread) I only want to get rid the scrollback while my app is running but when I exit I restore the saved original back. What do you mean by using the alt buffer? Resizing the buffer with Win API console app using conhost, cmd, powershell, conemu works well, but not on `Windows Terminal`. The only workaround that works on WT is using `"\x1b[3J"` escape sequence, but it shouldn't being necessary.
Author
Owner

@zadjii-msft commented on GitHub (Aug 9, 2023):

I only want to get rid the scrollback while my app is running but when I exit I restore the saved original back

Oh that's like, literally the whole idea of the "alternate screen buffer"! That's the thing that apps like vim, less, emacs, tmux, etc use to let them draw a full-window application, without a scrollback, and then dismiss it simply. Our docs are here. Basically, just SetConsoleMode with ENABLE_VT_PROCESSING, then print a "\x1b[?1049h" to enter the alt buffer, and exit it with "\x1b[?1049l". Pretty sure this sample (extremely dated) has an example of using the alt buffer from a c++ app.

@zadjii-msft commented on GitHub (Aug 9, 2023): > I only want to get rid the scrollback while my app is running but when I exit I restore the saved original back Oh that's like, literally the whole idea of the "alternate screen buffer"! That's the thing that apps like `vim`, `less`, `emacs`, `tmux`, etc use to let them draw a full-window application, without a scrollback, and then dismiss it simply. [Our docs are here](https://learn.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences#alternate-screen-buffer). Basically, just `SetConsoleMode` with `ENABLE_VT_PROCESSING`, then print a `"\x1b[?1049h"` to enter the alt buffer, and exit it with `"\x1b[?1049l"`. Pretty sure [this sample](https://learn.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences#example-of-select-anniversary-update-features) (_extremely dated_) has an example of using the alt buffer from a c++ app.
Author
Owner

@BDisp commented on GitHub (Aug 9, 2023):

Thanks for your attention. That was what I did for the NetDriver that essential use .NET System.Console for cross platform where for Windows I use Win API with ENABLE_VT_PROCESSING only for accepting escape sequences for keystrokes/mouse handle and for Linux is more straight way. On Windows it's very slower than on Linux. Only for Windows we have a WindowsDriver which basically use native Win API for keystroke/mouse handle, which is much more faster. Since it doesn't uses ENABLE_VT_PROCESSING only Windows Terminal recognizes escape sequences but not other terminal, which only recognize Win API. If Windows Terminal allows Win API functions on a Windows environment would be great. Believe me, conhost, cmd, powershell take a long time to interpret escape sequence, mainly with the mouse.

@BDisp commented on GitHub (Aug 9, 2023): Thanks for your attention. That was what I did for the [NetDriver](https://github.com/gui-cs/Terminal.Gui/blob/develop/Terminal.Gui/ConsoleDrivers/NetDriver.cs) that essential use .NET `System.Console` for cross platform where for `Windows` I use Win API with `ENABLE_VT_PROCESSING` only for accepting escape sequences for keystrokes/mouse handle and for `Linux` is more straight way. On `Windows` it's very slower than on `Linux`. Only for Windows we have a [WindowsDriver](https://github.com/gui-cs/Terminal.Gui/blob/develop/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs) which basically use native Win API for keystroke/mouse handle, which is much more faster. Since it doesn't uses `ENABLE_VT_PROCESSING` only `Windows Terminal` recognizes escape sequences but not other terminal, which only recognize Win API. If `Windows Terminal` allows Win API functions on a Windows environment would be great. Believe me, conhost, cmd, powershell take a long time to interpret escape sequence, mainly with the mouse.
Author
Owner

@BDisp commented on GitHub (Nov 5, 2024):

Thanks to the @lhecker, I finally learned how to appropriate use during app execution the screen buffer size always equal to the window size, by his words in https://github.com/microsoft/terminal/issues/18148#issuecomment-2455911978.

Generally speaking: Do not ever resize the buffer because the window was resized. This is true without the alternate screen-buffer but it's especially true if you do use it.

So, if we want to using the screen buffer size different of the window size, then we shouldn't ever use the alternate screen-buffer (ASB) at all.

@BDisp commented on GitHub (Nov 5, 2024): Thanks to the @lhecker, I finally learned how to appropriate use during app execution the screen buffer size always equal to the window size, by his words in https://github.com/microsoft/terminal/issues/18148#issuecomment-2455911978. `Generally speaking: Do not ever resize the buffer because the window was resized. This is true without the alternate screen-buffer but it's especially true if you do use it.` So, if we want to using the screen buffer size different of the window size, then we shouldn't ever use the alternate screen-buffer (ASB) at all.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/terminal#18180