Efficiently changing console buffer and window size and moving it around

Hi, still learning some winAPI tricks for the console, I got a function to change the console buffer and window size, however, it does not work correctly as I would expect. First, this is the function which was given to me:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void ScreenSize(int x, int y){

    COORD coord;
    coord.X = x;
    coord.Y = y;

    _SMALL_RECT Rect;
    Rect.Top = 0;
    Rect.Left = 0;
    Rect.Bottom = x-1;
    Rect.Right = y-1;

    HANDLE Handle = GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleScreenBufferSize(Handle, coord);
    SetConsoleWindowInfo(Handle, TRUE, &Rect);

}



It works well, but it only changes the buffer size. Also, when I try to input smaller numbers in the function arguments than those currently present, it does nothing. It doesn't change either the window size or the buffer size.

So, I know there are more correct ways to set the window to a specific size and also to move it around to my desires (which are also closely related in WinAPI I guess). Moving the window around is not that important, but being able to dynamically change the window size (to avoid the user having to manually scroll or expand the window to see the whole content) is most important for me now.

Thanks in advance.
There is a way to do it using system (I think.)

I have not tested this, but I saw this code pop up a few days ago on another post:
system("MODE CON COLS=25 LINES=22");//sets window size!

I think it will modify the console window size. Let me know if that works, while I begin to dig through the Windows API to look for another way.

P.S.: You will need to include cstdlib to get system to work.

Edit: On the SetConsoleWindowInfo page
http://msdn.microsoft.com/en-us/library/windows/desktop/ms686125(v=vs.85).aspx
There is a note:
The handle must have the GENERIC_READ access right. For more information, see Console Buffer Security and Access Rights.

Now I feel the need to go read about GetStdHandle to see if it gives GENERIC_READ access right by default.

Okay so it comes with those rights unless you have used SetStdHandle to set a standard handle with lesser access.

You seem to be doing everything right according to the API page. I would just double check by calling GetLargestConsoleWindowSize function to make sure those boundaries are allowed.
http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193(v=vs.85).aspx
Last edited on
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include <iostream>
#include <stdexcept>
#include <Windows.h>

void ScreenSize(int x, int y){

    COORD coord;
    coord.X = x;
    coord.Y = y;

    _SMALL_RECT Rect;
    Rect.Top = 0;
    Rect.Left = 0;
    Rect.Bottom = x - 1;
    Rect.Right = y - 1;

    // Adjust buffer size:
    HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);    
    if (!SetConsoleScreenBufferSize(handle, coord))
        throw std::runtime_error("Unable to resize screen buffer.");

    // display as a maximized window
    ShowWindow(GetConsoleWindow(), SW_MAXIMIZE);
}

void ShowLastSystemError()
{
    LPSTR messageBuffer;
    FormatMessageA(
        FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
        0,          // source
        GetLastError(),
        0,          // lang
        (LPSTR)&messageBuffer,
        0,
        NULL);

    std::cerr << messageBuffer << '\n';
    LocalFree(messageBuffer);
}

int main()
{
    try {
        ScreenSize(120, 80);
    }

    catch (std::exception& ex)
    {
        std::cerr << ex.what() << "\nSystem error: ";
        ShowLastSystemError();
    }
}
Thanks cire.

Your code easily set the window size and put it in the top left corner. However, Windows forced to change it back to default size if the window was manually moved afterwards.



I made a test with three changes one after the other with Sleep() between them, without manually touching the window:
In the first, I set x=80 and y=25 (default visible area) and it correctly changed the buffer size acordingly (Scroll bar dissapeared).
In the second, I set slightly higher numbers, and worked correctly.
In the third, I set it back to the defaults, and it gave me the following output:

Unable to resize screen buffer.
System error: The system cannot find the file specified.




The results seemed to be a little random, in another try with slightly different (if not the same) numbers:

In the first change, I set it to the defaults and it correctly set the buffer size.
In the second change, I set it to higher numbers but it only changed the buffer size.
In the third, I tried to put back the default visible area values and it changed back the buffer size correctly.




It seems it's not very stable. Maybe the Windows library is not supposed to be able to do this kind of stuff, I start to think.



Last edited on
Have a go with this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
#include <iostream>
#include <stdexcept>
#include <Windows.h>


void SetConsoleWindowSize(int x, int y)
{
    HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);

    if (h == INVALID_HANDLE_VALUE)
        throw std::runtime_error("Unable to get stdout handle.");

    // If either dimension is greater than the largest console window we can have,
    // there is no point in attempting the change.
    {
        COORD largestSize = GetLargestConsoleWindowSize(h);
        if (x > largestSize.X)
            throw std::invalid_argument("The x dimension is too large.");
        if (y > largestSize.Y)
            throw std::invalid_argument("The y dimension is too large.");
    }


    CONSOLE_SCREEN_BUFFER_INFO bufferInfo;
    if (!GetConsoleScreenBufferInfo(h, &bufferInfo))
        throw std::runtime_error("Unable to retrieve screen buffer info.");

    SMALL_RECT& winInfo = bufferInfo.srWindow;
    COORD windowSize = { winInfo.Right - winInfo.Left + 1, winInfo.Bottom - winInfo.Top + 1};

    if (windowSize.X > x || windowSize.Y > y)
    {
        // window size needs to be adjusted before the buffer size can be reduced.
        SMALL_RECT info = 
        { 
            0, 
            0, 
            x < windowSize.X ? x-1 : windowSize.X-1, 
            y < windowSize.Y ? y-1 : windowSize.Y-1 
        };

        if (!SetConsoleWindowInfo(h, TRUE, &info))
            throw std::runtime_error("Unable to resize window before resizing buffer.");
    }

    COORD size = { x, y };
    if (!SetConsoleScreenBufferSize(h, size))
        throw std::runtime_error("Unable to resize screen buffer.");


    SMALL_RECT info = { 0, 0, x - 1, y - 1 };
    if (!SetConsoleWindowInfo(h, TRUE, &info))
        throw std::runtime_error("Unable to resize window after resizing buffer.");
}



void ShowLastSystemError()
{
    LPSTR messageBuffer;
    FormatMessageA(
        FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
        0,          // source
        GetLastError(),
        0,          // lang
        (LPSTR)&messageBuffer,
        0,
        NULL);

    std::cerr << messageBuffer << '\n';
    LocalFree(messageBuffer);
}

COORD QueryUserForConsoleSize()
{
    COORD size = { 0, 0 };

    std::cout << "New console size: ";
    std::cin >> size.X >> size.Y;

    return size;
}

int main()
{
    COORD consoleSize;

    std::cout << "An x or y size of 0 will terminate the program\n";
    while (consoleSize = QueryUserForConsoleSize(), consoleSize.X && consoleSize.Y)
    {
        try {
            SetConsoleWindowSize(consoleSize.X, consoleSize.Y);
        }

        catch (std::logic_error& ex)
        {
            std::cerr << ex.what() << '\n';
        }

        catch (std::exception& ex)
        {
            std::cerr << ex.what() << "\nSystem error: ";
            ShowLastSystemError();
        }
    }
}
This is just... Amazing. Did you just write all that code?

It works more than perfectly. Many many thanks.
After many tries, I never got exceptions, and everything works as it should. I'll keep it in my code and for future projects of mine if I ever need it again.

You're awesome Cire.
This is just... Amazing. Did you just write all that code?


What I did that you didn't was... check return codes. That and a little experimentation/googling made short work of the code. It's not exactly perfect though. Information in the buffer is lost when you make the window smaller, for example.
Topic archived. No new replies allowed.