Skip to content

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

Open

Description

@Gyross

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.

rqUpmv2sfr.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.

Activity

  1. ghost added
    Needs-TriageIt's a new issue that the core contributor team needs to triage at the next triage meeting
    on Aug 15, 2022
  2. ghost added
    Needs-Tag-FixDoesn't match tag requirements
    on Aug 15, 2022
  3. Gyross commented on Aug 15, 2022

    @Gyross
    Author

    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?

  4. added
    Product-ConhostFor issues in the Console codebase
    Area-ServerDown in the muck of API call servicing, interprocess communication, eventing, etc.
    and removed
    Needs-TriageIt's a new issue that the core contributor team needs to triage at the next triage meeting
    Needs-Tag-FixDoesn't match tag requirements
    on Apr 26, 2023
  5. BDisp commented on Aug 8, 2023

    @BDisp

    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.

  6. zadjii-msft commented on Aug 9, 2023

    @zadjii-msft
    Member

    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)

  7. BDisp commented on Aug 9, 2023

    @BDisp

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

    Mike Griese (@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.

  8. zadjii-msft commented on Aug 9, 2023

    @zadjii-msft
    Member

    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.

  9. BDisp commented on Aug 9, 2023

    @BDisp

    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.

  10. BDisp commented on Nov 5, 2024

    @BDisp

    Thanks to the Leonard Hecker (@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 #18148 (comment).

    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 up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Area-ServerDown in the muck of API call servicing, interprocess communication, eventing, etc.Issue-BugIt either shouldn't be doing this or needs an investigation.Product-ConhostFor issues in the Console codebase

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions