繁体   English   中英

在 Windows 上设置控制台 window 大小

[英]Set console window size on Windows

我知道有很多关于如何设置控制台大小的问题。 但所有找到的解决方案对我来说都是一样的,我的代码对我不起作用。

好的,为了设置控制台 window 大小,我需要两个函数。 它们是SetConsoleScreenBufferSize()SetConsoleWindowInfo() 我的 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()不适用于所有值。 从文档:

指定的宽高不能小于控制台屏幕缓冲区的window的宽高

让我们尝试获取当前窗口的大小并调用我们的 function。要获取 window 大小,我需要GetConsoleScreenBufferInfo() function.main() 测试代码:

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);

在这种情况下SetConsoleScreenBufferSize()工作正常。 接下来 function 是SetConsoleWindowInfo() 这个 function 将在以下情况下工作:

如果指定的 window 矩形超出控制台屏幕缓冲区的边界,则 function 失败。 这意味着lpConsoleWindow矩形的TopLeft成员(或计算的顶部和左侧坐标,如果bAbsolute为 FALSE)不能小于零。 同样, BottomRight成员(或计算出的底部和右侧坐标)不能分别大于(屏幕缓冲区height – 1 )和(屏幕缓冲区width – 1 )。 如果Right成员(或计算的右坐标)小于或等于Left成员(或计算的左坐标)或者Bottom成员(或计算的底部坐标)小于或等于Top成员(或计算的顶部坐标)。

在我们的例子中,在调用GetConsoleScreenBufferInfo()之后,矩形的值与info.srWindow矩形的值相同(因为LeftTop为零)。 但! SetConsoleWindowInfo()失败,出现下一个::GetLastError()

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

如果我交换这两个函数的调用:

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);
}

那么我将有同样的错误。

那么,如何正确使用SetConsoleScreenBufferSize()SetConsoleWindowInfo()呢?

SetConsoleWindowInfo() 不会在屏幕上重新定位控制台窗口。 此函数的名称具有误导性。 它而是滚动控制台窗口内的当前可见部分。 在此处查看此示例

如果要设置运行程序的控制台窗口的位置,请使用以下代码:

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

TurboVision 端口

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;
}

我通过制作这些函数来解决这个问题,这些函数可以获取/设置控制台窗口/字符缓冲区大小,同时考虑到在需要时增加缓冲区大小、控制台字体大小、window 边框和所有爵士乐。

在这里发挥作用的变量要理解:

  • Windows 有一个client area ,这是不包括边框的坐标(以像素为单位)
  • Windows 有一个window area ,这是包括边界的坐标(以像素为单位)
  • 控制台有一个查看区域,大小为window个字符
  • 控制台有一个屏幕缓冲区,它是以字符为单位的可滚动缓冲区大小
  • 控制台有一个字体大小,这是坐标(像素)中的字符大小
  • 控制台的屏幕缓冲区不能小于视图区域

您需要正确地混合和匹配这些以达到预期的结果。

这些功能是即插即用的,因此您无需担心这些。

所有函数在成功时返回 TRUE (1),在出错时返回 FALSE (0)。

设置控制台 Window 尺寸

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);

获取控制台 Window 大小

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);

设置控制台缓冲区大小

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);

获取控制台缓冲区大小

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);

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM