3

I wrote a utility originally with Windows 7, which reads the entire console buffer and creates a text file with its contents. A stripped down version (no filing) is shown below, compiled as 32-bit with MSVC 2022.

On that machine the console buffer has 300 lines. The program outputs the following:

width  = 80
depth  = 300
buflen = 24000
nread  = 24000

Now, on a Windows 11 machine the console buffer is about 9000 lines, which can be verified by scrolling up. Yet only the current viewport is captured, and the output here is:

width  = 80
depth  = 25
buflen = 2000
nread  = 2000

I tried requesting more bytes to be read, but nread is still 2000. I've tried both GetConsoleScreenBufferInfo() and GetConsoleScreenBufferInfoEx() which give the same result.

Microsoft warns that ReadConsoleOutputCharacter should no longer be used, but is still supported. I also tried the sample code given by Scrolling a Screen Buffer's Window. This carries a similar warning, and again it works on Windows 7 but not on Windows 11.

The warnings advise using virtual terminal control sequences, but I don't see how they can be used to read the buffer.

How does one read the contents of the whole console buffer in Windows 11? Is it restricted by tighter security?

#define _CRT_SECURE_NO_WARNINGS
#define WIN32_LEAN_AND_MEAN 
    
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
    
void fail(const char *msg) {
    printf("%s\n", msg);
    exit(1);
}
    
int main(void) {
    // fill the console buffer
    for(int i = 10000; i > 0; i--)
        printf("%d\n", i);
    fflush(stdout);
    
    // get the console handle
    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
    if(hConsole == INVALID_HANDLE_VALUE)
        fail("Unable to get console handle");
    
    // get the console sizing
    CONSOLE_SCREEN_BUFFER_INFO info = {0};
    if(!GetConsoleScreenBufferInfo(hConsole, &info))
    //CONSOLE_SCREEN_BUFFER_INFOEX info = { sizeof(CONSOLE_SCREEN_BUFFER_INFOEX) };
    //if (!GetConsoleScreenBufferInfoEx(hConsole, &info))
        fail("Cannot get console size");
    size_t width = info.dwSize.X;
    size_t depth = info.dwSize.Y;
    
    // allocate memory
    size_t buflen = width * depth;
    LPTSTR buf = malloc(buflen);
    if(buf == NULL)
        fail("Cannot allocate buffer memory");
    
    // collect the console buffer
    COORD coord = {0, 0};
    DWORD nread = 0;
    if(!ReadConsoleOutputCharacter(hConsole, buf, buflen, coord, &nread))
        fail("Cannot read console buffer");
    
    // results
    printf("width  = %zu\n", width);
    printf("depth  = %zu\n", depth);
    printf("buflen = %zu\n", buflen);
    printf("nread  = %zu\n", (size_t)nread);
    
    // output to file . . . removed
    
    free(buf);
    return 0;
}
23
  • 1
    OK, not sure what the root cause is exactly. I do know the windows console changed for windows 11 -- "the default console is now "Windows Terminal," replacing the traditional "Command Prompt" as the primary interface for accessing command line..." And I recall that you can ( luckily ) change that behavior in windows settings. Please dig into it -- this might not be a code problem. Looks like your code is correct: dwSize is the proper structure member for getting the buffer size. Commented Dec 24, 2024 at 19:35
  • 1
    @gregspears I'm thinking this is perhaps a security enhancement, to retain some legacy compatibility but prevent apps from spying on the output of other apps. Also, it's complicated by Windows 11 providing two different command shells. Commented Dec 24, 2024 at 19:36
  • 1
    @gregspears that's not the problem, but when I "dig into it" I found that the utility works with administrator privilege. So I would always have to work like that, seeing as the utility is useful in hindsight. Commented Dec 24, 2024 at 20:28
  • 1
    @gregspears the code is suposed to work retrospectively, after the output has been made, when I need to capture it on those 'occasions'. The 9000 lines printed artificially set up known buffer state. Obviously the previous content won't be there. Commented Dec 24, 2024 at 20:36
  • 1
    Hi -- I ran everything in an administrator account. If lack of admin privilege is root cause, you needn't bother with my code. Thanks your kind & prompt reply! Commented Jul 14 at 17:26

1 Answer 1

2

This code is tested successfully compiling in MS Visual C and by running in a default Windows 11 OS install and Windows 10, too. Per my finding, not enough memory was allocated at runtime. The memory needed for the windows console buffer is computed:

UPDATED -- TCHAR:

buff_size = buffer_hgt * buffer_wdth * sizeof(TCHAR);

Pls see ReadConsoleOutputCharacter() concerning nLength [in] parameter. ReadConsoleOutputCharacter() does not capture character attributes ( as I previously erroneously thought ) -- but it does expect buffer capability to hold length * sizeof(TCHAR). TCHAR may (and often does) exceed 1 byte.

This new computation is the only significant change to the code, and it would not run until I made this buffer size correction. After numerous code adjustments and re-tests, the buffer size does appear to be root cause of the initial issue.

NOTES: Please note ( and apologies ) that this code is not portable and is specific to the MS windows OS per the question tag and OP's code and request.

#define _CRT_SECURE_NO_WARNINGS
#define WIN32_LEAN_AND_MEAN 
    
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
    
void fail(const char *msg) {
    printf("%s\n", msg);
    exit(1);
}
    
int main(void) 
{
    int i;
    HANDLE hConsole;
    LPTSTR buf;
    COORD coord = {0, 0};
    DWORD nread = 0, buflen, width, depth;
    CONSOLE_SCREEN_BUFFER_INFO info = {0};

    // fill the console buffer
    for(i = 10000; i > 0; i--)
        printf("%d\n", i);
    fflush(stdout);
    
    // get the console handle
    hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
    if(hConsole == INVALID_HANDLE_VALUE)
        fail("Unable to get console handle");
    
    // get the console sizing
    if(!GetConsoleScreenBufferInfo(hConsole, &info))
        fail("Cannot get console size");
    width = info.dwSize.X;
    depth = info.dwSize.Y;
    
    // allocate memory
    buflen =  width * depth;

    // GS UPDATE: factor in sizeof(TCHAR)
    buf = malloc(buflen * sizeof(TCHAR));  
    if(buf == NULL)
        fail("Cannot allocate buffer memory");
    
    // collect the console buffer
    // Note that parameter buflen still wants to be a product of only  
    //  width and height -- sizeof(TCHAR) is Not a factor.
    if(!ReadConsoleOutputCharacter(hConsole, buf, buflen, coord, &nread))
        fail("Cannot read console buffer");
    
    // results
    printf("width  = %lu\n", width);
    printf("depth  = %lu\n", depth);
    printf("buflen = %lu\n", buflen);
    printf("nread  = %lu\n", (size_t)nread);
    printf("sizeof(TCHAR) = %lu\n", sizeof(TCHAR)); //GS Added
    
    // output to file . . . removed
    free(buf);
    return 0;
}
Sign up to request clarification or add additional context in comments.

11 Comments

Thanks, and Happy Christmas! I have to go too and will look at this soon.
Thanks very much for your input: I only needed to have opened the console in Admin mode. If you were able to run the code on Windows 10, that is not Windows 11, which has additional security enhancements.
Thanks. In my system TCHAR is size 1. I'd overlooked that ReadConsoleOutputCharacter() takes a LPTSTR and not a LPCSTR but I never use wide characters.
I've returned for another look and got my code working without Admin privilege. The basic difference was that you have set the console buffer size (because you set up the buffer content from within the program). Remove that, and your code does not recognize the previously written console buffer (without admin). Resizing the buffer also affected the indexing of the original content, so I've drawn on your code for that too. It's strange that MS should protect the buffer from snooping like that, but leave a workaround! Many thanks for your input!
Thanks this feedback! I will remove the buffer-size-setting code and retest in a standard account, God willing.
... it was your other code versions (without T_CHAR) which sized the console buffer, and as I wrote, this version may have worked on Windows 10 without admin, but not on Windoows 11.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.