Click here to Skip to main content
Click here to Skip to main content

Writing to and read from the console - From a GUI application using the same cout/cin and printf/scanf

By , 23 Oct 2006
Rate this:
Please Sign up or sign in to vote.

Introduction

Hi folks, let me start with an interesting problem. I was developing an MFC dialog based application, and now I wanted to write some custom code in the OnPaint() function. Done! Unfortunately, in the first run, my application was blown up. I found that the problem was in the OnPaint() function. What next? Debug. Have you ever tried to debug the OnPaint() function? If not, try it yourself. If yes? I could see you scratching your head. Yes, it is very difficult to debug the OnPaint() or functions like OnMouseMove(). If we want to watch the value of a variable in these functions, the only way is to set up a remote debugger, but it needs some setup and an additional machine.

One other handy approach is to use the OutputDebugString() API to trace the value of a particular variable and monitor it with the help of DebugView (from SysInternals). The next thing I want to say is pretty interesting. Have you ever thought of writing to console from a GUI application? A good idea, right? Yes, it is possible to attach a console to a GUI application.

Attaching a console

We can use the standard Windows APIs such as AttachConsole() or AllocConsole() for attaching a console to our application. The difference between these two alternatives is that, the first needs a separate console application to which we can attach the current process. The AttachConsole() API has a process identifier as its parameter. So, we need the PID of the console application to which we need to attach. The next one, AllocConsole(), creates a new console for us and attaches the same to our process. It doesn’t need any argument, nor does it return any value. If succeeded, we can see a new console window waiting for input/output commands.

So far, every thing is fine! The next step is we have to issue the input/output commands to the attached console. We can retrieve the handles to the STDIN and STDOUT using the GetStdHandle() API. With the help of the ReadConsole/WriteConsole APIs, we can read from or write to the console. See the prototypes of these APIs in the MSDN.

BOOL ReadConsole(
  HANDLE hConsoleInput, // handle to console input buffer
  LPVOID lpBuffer, // data buffer
  DWORD nNumberOfCharsToRead, // number of characters to read
  LPDWORD lpNumberOfCharsRead, // number of characters read
  LPVOID lpReserved // reserved
);

BOOL WriteConsole(
  HANDLE hConsoleOutput, // handle to screen buffer
  CONST VOID *lpBuffer, // write buffer
  DWORD nNumberOfCharsToWrite, // number of characters to write
  LPDWORD lpNumberOfCharsWritten, // number of characters written
  LPVOID lpReserved // reserved
);

Frankly speaking, I don’t feel comfortable arranging the long set of parameters for each input/output command. It will be better if I could use the same cin, cout, scanf, printf commands when dealing with a console. But unfortunately, though you have successfully allocated a console (using AttachConsole or AllocConsole), the cin and cout or scanf and printf commands fail. Better you try it yourself. If we debug into and watch the contents of the stdin and stout (it is actually &_iobuf[0] and &_iobuf[1], respectively), we can see that the _file member of both are not initialized (-1). They should be normally 0 and 1, respectively. In order to use functions of the standard library, we have to initialize stdin and stout with the handle values of the newly allocated console. With the help of GetStdHandle(STD_INPUT_HANDLE), the handle to the standard input of the newly allocated console can be retrieved. We can get a file handle to STDIN with the _open_osfhandle() API. The _fdopen() API returns a FILE pointer to an already open stream (fpStdIn). Next, we have to replace stdin with the new FILE pointer.

*stdin = fpStdIn;

Similarly,

*stdout = fpStdOut;

This works fine for the scanf and printf functions. But, if before allocating a console, a cin or cout call is invoked, the subsequent calls after allocating the console will fail. The solution is to empty cin and cout just after allocating the console.

I have wrapped whatsoever we talked about into a simple class, ConsoleAdapter. It has mainly three functions:

  • CreateConsole – Allocates a new console with the help of the AllocConsole() API, and replaces the std handles with the help of the ReplaceHandles() function.
  • SpawnDumpConsole – Attaches to a new dump console specified as a parameter; a sample dummy console implementation is provided along with.
  • DestroyConsole – Detaches the console with the help of the FreeConsole() API. Once this function is invoked, the succeeding in/out calls will fail. In the case of the console created with SpawnConsole(), the spawned console application will be terminated.

While creating a ConsoleAdapter instance, you can specify whether the console needs to be automatically freed when the instance is deleted. By turning auto delete off, you have to invoke DestroyConsole() explicitly to destroy the console. The CreateConsole() and SpawnDumpConsole() accept a console type as an argument, i.e., you can specify an input, output, or both modes of the console (INPUT_CONS, OUTPUT_CONS, BOTH).

Only one console can be attached to a process at a time. So, you can use only any one of CreateConsole() or SpawnDumpConsole() at a time.

I have provided a sample dump console application whose path can be specified as a parameter for SpawnDumpConsole().

Sample screenshot

Code snippet

bool ConsoleAdapter::CreateConsole( CONSOLETYPE_e eConsoleType )
{
    try
    {
        m_eConsoleType = eConsoleType;
        AllocConsole();
        return ReplaceHandles();
    }
    catch ( ... )
    {
        return false;
    } 
}

bool ConsoleAdapter::SpawnDumpConsole( LPCTSTR lpctszDumConsoleApp, 
                                       CONSOLETYPE_e eConsoleType )
{
    try
    {
        m_eConsoleType = eConsoleType;
        STARTUPINFO stStartUpInfo = {0};
        PROCESS_INFORMATION stProcInfo = {0};
        if(!CreateProcess( lpctszDumConsoleApp,0,0,0,TRUE,
                           CREATE_NEW_CONSOLE, 
                           0,0,&stStartUpInfo, &stProcInfo )) 
        {
            return false;
        }
        m_hDumpConsole = stProcInfo.hProcess;
        // Waiting for the child process to be initialized

        Sleep( 100 );
        if(!AttachConsole( stProcInfo.dwProcessId ))
        {
            return false;
        }
        ReplaceHandles();
    }
    catch ( ... )
    {
        return false;
    }
    return true;
}

You need to put a small delay after CreateProcess(). This is because CreateProcess() returns TRUE before the child process is completely initialized.

bool ConsoleAdapter::ReplaceHandles()
{
    try
    {
        if( ( INPUT_CONS == m_eConsoleType ) || ( BOTH == m_eConsoleType ) )
        {
            m_nCRTIn= _open_osfhandle(
                     (long) GetStdHandle(STD_INPUT_HANDLE),
                      _O_TEXT );
            if( -1 == m_nCRTIn )
            {
                return false;
            }
            
            m_fpCRTIn = _fdopen( m_nCRTIn, "r" );
            
            if( !m_fpCRTIn )
            {
                return false;
            }
        
            m_fOldStdIn = *stdin;
            *stdin = *m_fpCRTIn;
            // if clear is not done, any cout

            // statement before AllocConsole

            // will cause, the cin after

            // AllocConsole to fail, so very important

            std::cin.clear();

        }

        if( ( OUTPUT_CONS == m_eConsoleType ) || 
            ( BOTH == m_eConsoleType ) )
        {
        
            m_nCRTOut= _open_osfhandle(
                        (long) GetStdHandle(STD_OUTPUT_HANDLE),
                        _O_TEXT );
            if( -1 == m_nCRTOut )
            {
                return false;
            }
    
            m_fpCRTOut = _fdopen( m_nCRTOut, "w" );
    
            if( !m_fpCRTOut )
            {
                return false;
            }
    
            m_fOldStdOut = *stdout;
            *stdout = *m_fpCRTOut;
    
            // if clear is not done, any cout

            // statement before AllocConsole

            // will cause, the cout after

            // AllocConsole to fail, so very important

            std::cout.clear();
        }
    
    }
    catch ( ... )
    {
        return false;
    } 
    
    return true;
}

The sample application provided demonstrates how you can use the ConsoleAdapter.

Using the ConsoleAdapter demo

  • Choose the console type: input, output, or both.
  • If you want to attach to the console of the DumpConsole provided, just browse the path of the DumpConsole.exe and click the Attach button. You will see a console as shown in the screenshot.
  • Or you can create a new console by clicking the “Create Console” button.
  • You can type the message to be displayed in the console, in the edit box provided to the left of the “Write To Console” button. When the “Write To Console” button is clicked, the message is displayed in the console.
  • Click the “Read From Console” button to read an input string from the console (you can read any data type, but here I am reading a string).

So, enjoy using the ConsoleAdapter. That’s all! Hope that ConsoleAdapter will be useful for you. Signing off for now.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Sudheesh.P.S
Architect
India India
Working in an MNC at Bangalore. I can be reached at sudheesh_perumbilli@yahoo.com.
My personal page http://mytechcraze.wordpress.com/

Comments and Discussions

 
QuestionThe ReplaceHandle function fails if AttachConsole is called with Pinmemberrsprenge21-Sep-12 14:15 
GeneralSome doubt PinmemberMember 396209426-May-08 21:38 
GeneralA bug in the Console Pinmemberflexed27-Apr-08 9:49 
General_close( m_nCRTOut ); fails Pinmemberkdl25-Nov-07 2:16 
GeneralRe: _close( m_nCRTOut ); fails PinmemberSudheesh.P.S27-Nov-07 2:01 
Generalwant to debug windows service/know the status Pinmemberpraveenrn12-Oct-07 1:46 
GeneralHello PinmemberSarath.4-Apr-07 16:32 
QuestionCan't read console Pinmember12kaunas30-Mar-07 7:09 
AnswerRe: Can't read console PinmemberSudheesh.P.S4-Apr-07 3:23 
GeneralDebuging OnPaint... simple PinmemberJaded Hobo11-Nov-06 4:07 
Just use a second monitor!
 
Of course a small console window is always handy Smile | :)
QuestionI need your help Pinmemberhooh_group8-Nov-06 19:35 
AnswerRe: I need your help PinmemberSudheesh.P.S18-Nov-06 21:49 
GeneralGood, but... PinmemberXoXoXo16-Oct-06 3:57 
GeneralThanks... PinmemberDavidCrow10-Oct-06 2:54 
GeneralWell worth reading PinmemberDave Cross10-Oct-06 0:39 
GeneralGood article, and a tip for .NET apps Pinmemberdfellman6-Oct-06 6:46 
GeneralRe: Good article, and a tip for .NET apps Pinmemberdwangs10-Oct-06 5:47 
GeneralReformat your article ... PinmemberAnonymuos6-Oct-06 5:43 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140415.2 | Last Updated 24 Oct 2006
Article Copyright 2006 by Sudheesh.P.S
Everything else Copyright © CodeProject, 1999-2014
Terms of Use
Layout: fixed | fluid