grisha
grisha

Reputation: 1297

Set console window size on Windows

I know that there is a lot questions about how to set console size. But all found solutions are the same to my and my code doesn't works for me.

Ok, so for setting console window size, I need two functions. They are SetConsoleScreenBufferSize() and SetConsoleWindowInfo(). First version of my function:

bool SetWindowSize(size_t width, size_t height)
{
    HANDLE output_handle = ::GetStdHandle(STD_OUTPUT_HANDLE);
    if(output_handle == INVALID_HANDLE_VALUE)
        return false;

    COORD coord = {};
    coord.X = static_cast<SHORT>(width);
    coord.Y = static_cast<SHORT>(height);
    if(::SetConsoleScreenBufferSize(output_handle, coord) == FALSE)
        return false;

    SMALL_RECT rect = {};
    rect.Bottom = coord.X - 1;
    rect.Right = coord.Y - 1;
    return (::SetConsoleWindowInfo(output_handle, TRUE, &rect) != FALSE);
}

SetConsoleScreenBufferSize() will work not for all values. From documentation:

The specified width and height cannot be less than the width and height of the console screen buffer's window

Lets try to get current window's size and call our function. To get window size, I need GetConsoleScreenBufferInfo() function. main() test code:

HANDLE output_handle = ::GetStdHandle(STD_OUTPUT_HANDLE);
if(output_handle == INVALID_HANDLE_VALUE)
    return 0;
CONSOLE_SCREEN_BUFFER_INFO info = {};
if(::GetConsoleScreenBufferInfo(output_handle, &info) == FALSE)
    return 0;
size_t width = info.srWindow.Right - info.srWindow.Left;
size_t height = info.srWindow.Bottom - info.srWindow.Top;
bool suc = SetWindowSize(width + 1, height + 1);

In this case SetConsoleScreenBufferSize() works fine. Next function is SetConsoleWindowInfo(). This function will work in case:

The function fails if the specified window rectangle extends beyond the boundaries of the console screen buffer. This means that the Top and Left members of the lpConsoleWindow rectangle (or the calculated top and left coordinates, if bAbsolute is FALSE) cannot be less than zero. Similarly, the Bottom and Right members (or the calculated bottom and right coordinates) cannot be greater than (screen buffer height – 1) and (screen buffer width – 1), respectively. The function also fails if the Right member (or calculated right coordinate) is less than or equal to the Left member (or calculated left coordinate) or if the Bottom member (or calculated bottom coordinate) is less than or equal to the Top member (or calculated top coordinate).

In our case, the values of rectangle are the same (because Left and Top are zeroes) as values of info.srWindow rectangle after call of GetConsoleScreenBufferInfo(). But! SetConsoleWindowInfo() fails with next ::GetLastError()

@err,hr ERROR_INVALID_PARAMETER : The parameter is incorrect.   unsigned int

If I swap calls of this two functions:

bool SetWindowSize(size_t width, size_t height)
{
    HANDLE output_handle = ::GetStdHandle(STD_OUTPUT_HANDLE);
    if(output_handle == INVALID_HANDLE_VALUE)
        return false;

    SMALL_RECT rect = {};
    rect.Bottom = static_cast<SHORT>(width);
    rect.Right = static_cast<SHORT>(height);
    if(::SetConsoleWindowInfo(output_handle, TRUE, &rect) == FALSE)
        return false;

    COORD coord = {};
    coord.X = rect.Bottom + 1;
    coord.Y = rect.Right + 1;

    return (::SetConsoleScreenBufferSize(output_handle, coord) != FALSE);
}

then I will have the same error.

So, how can I use SetConsoleScreenBufferSize() and SetConsoleWindowInfo() correctly ?

Upvotes: 3

Views: 18056

Answers (3)

anzz1
anzz1

Reputation: 121

I solved this issue by making these functions which can get/set the console window/buffer sizes in characters taking into account increasing the buffer size if needed, console font size, window borders and all that jazz.

The variables at play here to understand:

  • Windows have a client area, which is the coordinates (in pixels) excluding the borders
  • Windows have a window area, which is the coordinates (in pixels) including the borders
  • Console has a view area, which is the window size in characters
  • Console has a screen buffer, which is the scrollable buffer size in characters
  • Console has a font size, which is the character size in coordinates (pixels)
  • Console's screen buffer cannot be smaller than the view area

You need to correctly mix and match these around to achieve the desired result.

These functions are plug-n-play so you don't need to worry about none of that though.

All functions return TRUE (1) on success and FALSE (0) on error.

Set Console Window Size

static BOOL SetConsoleSize(int cols, int rows) {
  HWND hWnd;
  HANDLE hConOut;
  CONSOLE_FONT_INFO fi;
  CONSOLE_SCREEN_BUFFER_INFO bi;
  int w, h, bw, bh;
  RECT rect = {0, 0, 0, 0};
  COORD coord = {0, 0};
  hWnd = GetConsoleWindow();
  if (hWnd) {
    hConOut = GetStdHandle(STD_OUTPUT_HANDLE);
    if (hConOut && hConOut != (HANDLE)-1) {
      if (GetCurrentConsoleFont(hConOut, FALSE, &fi)) {
        if (GetClientRect(hWnd, &rect)) {
          w = rect.right-rect.left;
          h = rect.bottom-rect.top;
          if (GetWindowRect(hWnd, &rect)) {
            bw = rect.right-rect.left-w;
            bh = rect.bottom-rect.top-h;
            if (GetConsoleScreenBufferInfo(hConOut, &bi)) {
              coord.X = bi.dwSize.X;
              coord.Y = bi.dwSize.Y;
              if (coord.X < cols || coord.Y < rows) {
                if (coord.X < cols) {
                  coord.X = cols;
                }
                if (coord.Y < rows) {
                  coord.Y = rows;
                }
                if (!SetConsoleScreenBufferSize(hConOut, coord)) {
                  return FALSE;
                }
              }
              return SetWindowPos(hWnd, NULL, rect.left, rect.top, cols*fi.dwFontSize.X+bw, rows*fi.dwFontSize.Y+bh, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER);
            }
          }
        }
      }
    }
  }
  return FALSE;
}

/* usage */
SetConsoleSize(80, 40);

Get Console Window Size

static BOOL GetConsoleSize(int* cols, int* rows) {
  HWND hWnd;
  HANDLE hConOut;
  CONSOLE_FONT_INFO fi;
  int w, h;
  RECT rect = {0, 0, 0, 0};
  hWnd = GetConsoleWindow();
  if (hWnd) {
    hConOut = GetStdHandle(STD_OUTPUT_HANDLE);
    if (hConOut && hConOut != (HANDLE)-1) {
      if (GetCurrentConsoleFont(hConOut, FALSE, &fi)) {
        if (GetClientRect(hWnd, &rect)) {
          w = rect.right-rect.left;
          h = rect.bottom-rect.top;
          *cols = w / fi.dwFontSize.X;
          *rows = h / fi.dwFontSize.Y;
          return TRUE;
        }
      }
    }
  }
  return FALSE;
}

/* usage */
int cols, rows;
GetConsoleSize(&cols, &rows);

Set Console Buffer Size

static BOOL SetConsoleBufferSize(int cols, int rows) {
  HANDLE hConOut;
  CONSOLE_SCREEN_BUFFER_INFO bi;
  COORD coord = {0, 0};
  hConOut = GetStdHandle(STD_OUTPUT_HANDLE);
  if (hConOut && hConOut != (HANDLE)-1) {
    if (GetConsoleScreenBufferInfo(hConOut, &bi)) {
      coord.X = cols;
      coord.Y = rows;
      return SetConsoleScreenBufferSize(hConOut, coord);
    }
  }
  return FALSE;
}

/* usage */
SetConsoleBufferSize(80, 300);

Get Console Buffer Size

static BOOL GetConsoleBufferSize(int* cols, int* rows) {
  HANDLE hConOut;
  CONSOLE_SCREEN_BUFFER_INFO bi;
  hConOut = GetStdHandle(STD_OUTPUT_HANDLE);
  if (hConOut && hConOut != (HANDLE)-1) {
    if (GetConsoleScreenBufferInfo(hConOut, &bi)) {
      *cols = bi.dwSize.X;
      *rows = bi.dwSize.Y;
      return TRUE;
    }
  }
  return FALSE;
}

/* usage */
int cols, rows;
GetConsoleBufferSize(&cols, &rows);

Upvotes: 3

RED SOFT ADAIR
RED SOFT ADAIR

Reputation: 12218

SetConsoleWindowInfo() does not reposition the console window on the screen. The name of this function is misleading. It rather scrolls the current visible portion inside the console window. See this sample here.

If you want to set the position of a console window that runs your programm, use code such as:

HWND hwnd = GetConsoleWindow();
RECT rect = {100, 100, 300, 500};
MoveWindow(hwnd, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top,TRUE);

Upvotes: 7

Igor Skochinsky
Igor Skochinsky

Reputation: 25268

From the TurboVision port:

void TDisplay::setCrtMode( ushort mode )
{
  int oldr = getRows();
  int oldc = getCols();
  int cols = uchar(mode >> 8);
  int rows = uchar(mode);
  if ( cols == 0 ) cols = oldc;
  if ( rows == 0 ) rows = oldr;
  checksize(rows, cols);
  COORD newSize = { cols, rows };
  SMALL_RECT rect = { 0, 0, cols-1, rows-1 };

  if ( oldr <= rows )
  {
    if ( oldc <= cols )
    {                           // increasing both dimensions
BUFWIN:
      SetConsoleScreenBufferSize( TThreads::chandle[cnOutput], newSize );
      SetConsoleWindowInfo( TThreads::chandle[cnOutput], True, &rect );
    }
    else
    {                           // cols--, rows+
      SMALL_RECT tmp = { 0, 0, cols-1, oldr-1 };
      SetConsoleWindowInfo( TThreads::chandle[cnOutput], True, &tmp );
      goto BUFWIN;
    }
  }
  else
  {
    if ( oldc <= cols )
    {                           // cols+, rows--
      SMALL_RECT tmp = { 0, 0, oldc-1, rows-1 };
      SetConsoleWindowInfo( TThreads::chandle[cnOutput], True, &tmp );
      goto BUFWIN;
    }
    else
    {                           // cols--, rows--
      SetConsoleWindowInfo( TThreads::chandle[cnOutput], True, &rect );
      SetConsoleScreenBufferSize( TThreads::chandle[cnOutput], newSize );
    }
  }
  GetConsoleScreenBufferInfo( TThreads::chandle[cnOutput], &TThreads::sbInfo );
}

ushort TDisplay::getRows()
{
  GetConsoleScreenBufferInfo( TThreads::chandle[cnOutput], &TThreads::sbInfo );
  return TThreads::sbInfo.dwSize.Y;
}

ushort TDisplay::getCols()
{
  GetConsoleScreenBufferInfo( TThreads::chandle[cnOutput], &TThreads::sbInfo );
  return TThreads::sbInfo.dwSize.X;
}

Upvotes: 1

Related Questions