Search code examples
cwindowsfunctionwinapiconsole

GetConsoleScreenBufferInfo returning wierd values when called using function


I have a program where I want to print out the size of the terminal window in columns and rows.

To do this I want to call this function from int main():

int getConsoleSize(){
    CONSOLE_SCREEN_BUFFER_INFO csbi;
    int columns, rows;

    GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
    columns = csbi.srWindow.Right - csbi.srWindow.Left + 1;
    rows = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;

    return columns, rows;
}

int main() looks like this:

int main(){
    int columns, rows = getConsoleSize();
    printf("columns: %d\n", columns);
    printf("rows: %d\n", rows);

    //prevent window from closing
    getc(stdin);

    return 0;
}

When calling the function getConsoleSize() it gives me the output columns: 5990652\n rows: 30 which is clearly not correct. The columns output is also not consistent but is always at least 6-7 numbers long. But when the code is copy pasted from the function directly into main() it seems to return the correct values with the outputs columns: 120\n rows: 30.

Why is this and how do I fix it so that the function return the correct values.


Solution

  • You cannot return 2 values like that from the function getConsoleSize.

    The function is returning only 1 int - the rows in this case.
    columns is main remains uninitialized and what you see is a "garbaged" value.

    In order to get both dimensions you can change GetConsoleSize to get 2 parameters as int* (pointer to int), and dereference the pointers to update the dimensions in main.

    You should also check the return value from GetConsoleScreenBufferInfo to verify it succeeded.

    The success/failure status should also be returned from GetConsoleSize as a boolean and checked in main.

    #include <Windows.h>
    #include <stdio.h>
    
    BOOL GetConsoleSize(int * cols, int * rows) 
    {
        CONSOLE_SCREEN_BUFFER_INFO csbi;
        if (!GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi))
        {
            return FALSE;
        }
        // TODO: make sure cols, rows are not NULL
        *cols = csbi.srWindow.Right - csbi.srWindow.Left + 1;
        *rows = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
        return TRUE;
    }
    
    int main() 
    {
        int columns, rows;
        if (!GetConsoleSize(&columns, &rows))
        {
            printf("FAILED\n");
            return EXIT_FAILURE;
        }
        printf("columns: %d\n", columns);
        printf("rows: %d\n", rows);
        return EXIT_SUCCESS;
    }
    

    Possible output:

    columns: 120
    rows: 30
    

    As commented below, another option is to return a struct with rows and columns fields. A sentinel can be used to report an error:

    #include <Windows.h>
    #include <stdio.h>
    
    typedef struct
    {
        int cols;
        int rows;
    } Size;
    
    const Size  InvalidSize = { -1, -1 };
    
    BOOL IsSameSize(Size lhs, Size rhs)
    {
        return (lhs.cols == rhs.cols && lhs.rows == rhs.rows);
    }
    
    Size GetConsoleSize()
    {
        CONSOLE_SCREEN_BUFFER_INFO csbi;
        if (!GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi))
        {
            return InvalidSize;
        }
        Size result;
        result.cols = csbi.srWindow.Right - csbi.srWindow.Left + 1;
        result.rows = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
        return result;
    }
    
    int main()
    {
        Size result = GetConsoleSize();
        if (IsSameSize(result, InvalidSize))
        {
            printf("FAILED\n");
            return EXIT_FAILURE;
        }
        printf("columns: %d\n", result.cols);
        printf("rows: %d\n", result.rows);
        return EXIT_SUCCESS;
    }
    

    Note: I consider the first solution more idiomatic in Windows environment, but the second one is valid as well (and has the advatage that there's no need to check that the passed pointers are not NULL).