How to output struct CHAR_INFO contains

How to output struct CHAR_INFO contains?
CHAR_INFO is a Windows struct used with console programming. It is defined as:

1
2
3
4
5
6
7
typedef struct _CHAR_INFO {
  union {
    WCHAR UnicodeChar;
    CHAR  AsciiChar;
  } Char;
  WORD  Attributes;
} CHAR_INFO, *PCHAR_INFO;


So a trivial C++ example is:

1
2
3
4
5
CHAR_INFO ci;

ci.Char.AsciiChar = 'C';

std::cout << ci.Char.AsciiChar << '\n';

Last edited on
It works for CHAR_INFO ci;
It does not work for CHAR_INFO chiBuffer[160]
trying to read data got from terminal
https://learn.microsoft.com/en-us/windows/console/reading-and-writing-blocks-of-characters-and-attributes?redirectedfrom=MSDN
How do you know if you should read AsciiChar or UnicodeChar?
It does not work for CHAR_INFO chiBuffer[160]


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <Windows.h>

#include <iostream>

int main() {
	CHAR_INFO chiBuffer[160] {};

	for (unsigned i {}; i < 160; ++i)
		chiBuffer[i].Char.AsciiChar = ' ' + i % 95;

	for (unsigned i {}; i < 160; ++i)
		std::cout << chiBuffer[i].Char.AsciiChar;

	std::cout << '\n';
}


which displays:


 !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`


Post your code if you're got issues.

How do you know if you should read AsciiChar or UnicodeChar?


That's down to you if you're compiling as unicode or multi-byte.
I'm struggling to save to string output from terminal
https://learn.microsoft.com/en-us/windows/console/reading-and-writing-blocks-of-characters-and-attributes?redirectedfrom=MSDN

Your function works, but gives no effect

Can you please give me a clue how to handle this?
Windows:
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
#include <iostream>
#include <windows.h>

std::ostream& operator << ( std::ostream& stm, CHAR_INFO ch ) { return stm << ch.Char.AsciiChar ; } // narrow character stream
std::wostream& operator << ( std::wostream& wstm, CHAR_INFO ch ) { return wstm << ch.Char.UnicodeChar ; } // wide character stream

int main()
{
    const HANDLE h_stdout = ::GetStdHandle(STD_OUTPUT_HANDLE) ;

    if( h_stdout != INVALID_HANDLE_VALUE )
    {
        // send some output to the console (for testing)
        std::cout << "'Twas brillig, and the slithy toves\n"
                     "Did gyre and gimble in the wabe:\n"
                     "All mimsy were the borogoves,\n"
                     "And the mome raths outgrabe.\n" ;

        const std::size_t NROWS = 40 ;
        const std::size_t NCOLS = 120 ;
        ::CHAR_INFO buffer[NROWS*NCOLS] {} ;  // NROWSxNCOLS

        // read console screen contents into the buffer
        ::SMALL_RECT rect {0,0,NCOLS-1,3} ; // the console screen (buffer) region to read from (120x4)
        if( ::ReadConsoleOutput( h_stdout, buffer, {NCOLS,NROWS} /*buffer size colsxrows*/, {0,0} /*buffer top,left*/, &rect ) )
        {
            // note: use the contents of the updated rect for printing (it would contain the region that was actually read)

            // print it out as narrow characters
            std::cout << "\n---------------------\nbuffer contains (char)\n---------------------\n" ;
            for( std::size_t row = rect.Top ; row <= rect.Bottom ; ++row )
               for( std::size_t col = rect.Left ; col <= rect.Right ; ++col ) std::cout << buffer[row*NCOLS+col] ;

            // print it out as wide characters
            std::wcout << L"\n---------------------\nbuffer contains (wchar_t)\n---------------------\n" ;
            for( std::size_t row = rect.Top ; row <= rect.Bottom ; ++row )
               for( std::size_t col = rect.Left ; col <= rect.Right ; ++col ) std::wcout << buffer[row*NCOLS+col] ;
        }
    }
}
Your function works, but gives no effect


As asked before, please post your code.
> I'm struggling to save to string output from terminal

The tee (between std::cout and string stream) technique would yield a historical record of the output
performed through std::cout. But it would ignore output to the console by any other means.

The ReadConsoleOutput (into an array of CHAR_INFO) technique would yield a snapshot of the
contents of the console at that point in time. But it would ignore history; for instance it would miss out on the
text that has scrolled off the console buffer.

This program illustrates the difference between the two approaches:

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
#include <iostream>
#include <windows.h>
#include <cstdlib>
#include <sstream>
#include <string>
#include <cstdio>

namespace utility
{
    template < typename C, typename T = std::char_traits<C> >
    struct basic_teebuf : public std::basic_streambuf<C,T>
    {
        typedef std::basic_streambuf<C,T> streambuf_type;
        typedef typename T::int_type int_type;

        basic_teebuf( streambuf_type* buff_a, streambuf_type* buff_b )
            : first(buff_a), second(buff_b) {}

        protected:
            virtual int_type overflow( int_type c )
            {
                const int_type eof = T::eof() ;
                if( T::eq_int_type( c, eof ) ) return T::not_eof(c) ;
                else
                {
                    const typename T::char_type ch = T::to_char_type(c) ;
                    return T::eq_int_type( first->sputc(ch), eof ) ||
                           T::eq_int_type( second->sputc(ch), eof ) ? eof : c ;
                }
            }

            virtual int sync() { return !first->pubsync() && !second->pubsync() ? 0 : -1 ; }

        private:
            streambuf_type* first ;
            streambuf_type* second ;
    };

    typedef basic_teebuf<char> teebuf;
}

int main()
{
    // set up a tee to also send output of stdout to a string buffer
    std::stringbuf sbuf( std::ios_base::out ) ;
    std::streambuf* const oldbuf = std::cout.rdbuf() ;
    utility::teebuf tbuf( oldbuf, &sbuf ) ;
    std::cout.rdbuf( &tbuf ) ;

    // and some output from this program
    std::cout << "line #1: " << 1234 << '\n'
              << "line #2: " << 56.78 << '\n'
              << "line #3: " << 999 << ' ' << 999.999 << '\n' << std::flush ;

    std::cout << "press enter to continue... " ;
    std::cin.get() ;

    std::system( "cls && dir /OS" ) ; // clear the screen and send some fresh (external) output to the console
    std::cout << "------ output from this program through std::cout ----\n" << std::flush ; // and some output from this program (std::cout)
    std::puts( "------ and some more output from this program using cstdio ----" ) ; // and some more output from this program (cstdio)

    std::cout.rdbuf(oldbuf) ; // restore old behaviour of cout

    const HANDLE h_stdout = ::GetStdHandle(STD_OUTPUT_HANDLE) ;
    if( h_stdout != INVALID_HANDLE_VALUE )
    {
        const std::size_t NROWS = 40 ;
        const std::size_t NCOLS = 120 ;
        ::CHAR_INFO buffer[NROWS][NCOLS] ;  // NROWSxNCOLS

        // read the *current* console screen contents into the buffer (read what is currently in the console)
        ::SMALL_RECT rect {0,0,NCOLS-1,24} ; // the console screen (buffer) region to read from (120x25)
        if( ::ReadConsoleOutput( h_stdout, buffer[0], {NCOLS,NROWS} /*buffer size colsxrows*/, {0,0} /*buffer top,left*/, &rect ) )
        {
            // retrieve the *current* text in the console into a string. note: use the contents of the updated rect
            std::string console_text ;
            for( std::size_t row = rect.Top ; row <= rect.Bottom ; ++row )
               for( std::size_t col = rect.Left ; col <= rect.Right ; ++col ) console_text += buffer[row][col].Char.AsciiChar ;

            std::cout << "\n\n---------------------\nstring retrieved through ReadConsoleOutput\n---------------------\n" << console_text ;
        }
    }

    const std::string tee_text = sbuf.str() ; // retrieve the string from the string buffer (gets what was written using std::cout)
    std::cout << "\n\n---------------------\nstring retrieved from the teed string stream\n---------------------\n" << tee_text ;
}
Topic archived. No new replies allowed.