2

I had the idea of using a second console in my programs for the purpose of logging programming activity. I looked around on msdn for related functions/examples and tried to put together a simple program to do so:

//function in parent that communicates with child
void writeToChild()
{
STARTUPINFO si;
PROCESS_INFORMATION pi;

HANDLE inWrite, inRead,
       outWrite, outRead;
SECURITY_ATTRIBUTES saAttr;

saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
if (!CreatePipe(&inRead, &inWrite, &saAttr, 0))
{
    exit(0xbad);
}
if ( ! SetHandleInformation(inRead, HANDLE_FLAG_INHERIT, 0) )
{
    exit(0xbad);
}
if (!CreatePipe(&outRead, &outWrite, &saAttr, 0))
{
    exit(0xbad);
}
if ( ! SetHandleInformation(outRead, HANDLE_FLAG_INHERIT, 0) )
{
    exit(0xbad);
}

ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);
si.lpTitle = "Log";
si.dwFlags = STARTF_USESTDHANDLES;
si.hStdOutput = outWrite;
si.hStdError = outWrite;
si.hStdInput = inRead;
ZeroMemory( &pi, sizeof(pi) );

// Start the child process.
if( !CreateProcess( NULL,   
    "Logger",          
    NULL,        
    NULL,       
    TRUE,       
    CREATE_NEW_CONSOLE,             
    NULL,           
    NULL,           
    &si,            
    &pi )          
)
{
    printf( "CreateProcess failed (%d).\n", GetLastError() );
    exit(0xbad);
}
unsigned long numWritten = 0;
WriteFile(inWrite, "Test", 4, &numWritten, NULL);
cout<<"wrote "<<numWritten<<" characters"<<endl;

// Wait until child process exits.
WaitForSingleObject( pi.hProcess, INFINITE );

// Close process and thread handles.
CloseHandle( pi.hProcess );
CloseHandle( pi.hThread );
}

//Logger
#include <windows.h>

#define BUFSIZE 4096

int main()
{
HANDLE stdin, stdout;
stdin = GetStdHandle(STD_INPUT_HANDLE);
stdout = GetStdHandle(STD_OUTPUT_HANDLE);

char buffer[BUFSIZE];
unsigned long numRead;
while (true)
{
    ReadFile(stdin, buffer, BUFSIZE, &numRead, NULL);
    if (numRead > 0)
        WriteFile(stdout, buffer, numRead, NULL, NULL);
}
return 0;
}

The problem is that while the parent displays that 4 characters were written, nothing appears on the child console. I'm not sure how to go about debugging this because I have next to no experience with windows programming.

Bryan
  • 11,398
  • 3
  • 53
  • 78
Niven
  • 121
  • 1
  • Your question is "How do I use a second console?" but your code has a whole bunch of stuff with a child process in it that is not mentioned in your description. Can you please clarify what you are trying to do? – Jason C Nov 24 '14 at 20:39
  • To be honest, the code I had so far wasn't the best and I frustratingly poked and prodded at it so much trying to get something working, that by the end I wasn't even sure what it was supposed to do. The original idea was to have a second console for keeping a log of program behavior and use the main console for user interaction. I read your answer, and it was very helpful. I'm going to use SetConsoleActiveSceenBuffer and switch between the two with tab which will work just as well for my purposes, so thank you very much. – Niven Nov 24 '14 at 21:54
  • Does this answer your question? [Multiple consoles for a single application C++](https://stackoverflow.com/questions/20847474/multiple-consoles-for-a-single-application-c) – schlebe Jan 12 '23 at 07:26

2 Answers2

2

I am not quite sure where you are going with the separate process in your code snippet. However, you can't have more than one console associated with a single process in Windows, although you can have multiple screen buffers associated with a single console and toggle between them (see SetConsoleActiveScreenBuffer and go from there), but you'll have to implement a user-interface for that toggling; it's not a built-in thing.

If the screen buffers do not work for you, you could have a second logger process that you communicate with via pipes or loopback sockets or some other IPC method, though, e.g.:

  • You can use syslog, which is a common logging facility used by various applications and hardware.

  • You can also write to a log file and use a program to watch the file, e.g. the Windows equivalent of tail -f. This has the bonus of storing the logged data in a file for easy review later.

  • You could have your application act as a TCP server, telnet to it, and dump log messages via telnet.

Note that for any of the above options, as a convenience to your user, you can have your application start the second log watching process separately (with just a simple call to ShellExecute, don't go overboard).

Other options include:

  • You could use the Windows event log, although it can be a bit cumbersome.

  • You could create a separate GUI window with a text field or a list in it that displays log messages.

  • If your program is a GUI application that doesn't have its own console (from your description, this does not seem to be the case, but just for completeness) you can create a single console for it with AllocConsole and friends (but note that closing that console while it is still attached will also terminate your application).

There are many choices here. But you can't have two consoles for the same process.

Community
  • 1
  • 1
Jason C
  • 38,729
  • 14
  • 126
  • 182
0

Here is a summary of how I've managed to do it:

//parent process
YourString pipeName = format(L"\\\\.\\pipe\\%s", pName);

HANDLE hPipe = CreateNamedPipe(pipeName, /*dwOpenMode*/PIPE_ACCESS_OUTBOUND, 
    /*dwPipeMode*/PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT | PIPE_REJECT_REMOTE_CLIENTS,
    /*nMaxInstances*/PIPE_UNLIMITED_INSTANCES,
    /*nOutBufferSize*/10000, /*nInBufferSize*/10000, /*nDefaultTimeOut*/50, 
    /*lpSecurityAttributes*/NULL);
//[check return value]

PROCESS_INFORMATION pi = {};
STARTUPINFOW si = {};

YourString commandLine = format(L"\"%s\" \"%s\"", childProcessExeFileName, pipeName);

HANDLE hProcess = CreateProcess(
        /*lpApplicationName*/NULL, /*lpCommandLine*/commandLine,
        /*lpProcessAttributes*/NULL, /*lpThreadAttributes*/NULL,
        /*bInheritHandles*/TRUE, /*dwCreationFlags*/CREATE_NEW_CONSOLE,
        /*lpEnvironment*/NULL, /*lpCurrentDirectory*/NULL,
        &si, &pi);
//[check return value]

lpBuffer = ...
nNumberOfBytesToWrite = len(lpBuffer);

WriteFile(hPipe, lpBuffer, nNumberOfBytesToWrite, /*lpNumberOfBytesWritten*/NULL, /*lpOverlapped*/NULL);




//child process
int main(int argc, char ** argv) {

    HANDLE hPipe = CreateFile(argv[1], GENERIC_READ, 0, 
        NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    //[check return value]

    ULONG PID;
    BOOL ok = GetNamedPipeServerProcessId(hPipe, &PID); 
    //[check return value]

    HANDLE hProcess = OpenProcess(SYNCHRONIZE, FALSE, PID);
    //[check return value]

    char buffer[1000];
    DWORD nByteRead;

    while (WaitForSingleObject(hProcess, 0) == WAIT_TIMEOUT) {

        if (hPipe) {

            ok = ReadFile(hPipe, buffer,
                sizeof(buffer) - 1, &nByteRead, NULL);

            buffer[nByteRead] = 0;

            if (!ok) {
                if (GetLastError() != ERROR_BROKEN_PIPE) ERROR();
                hPipe = NULL;
            }
            else printf("%s", buffer);
        }
        else Sleep(1000);
    }

    return 1;
}

If you are interested I could send you a class that uses this, with a simple syntax:

WinConsole secondConsole;
secondConsole.Printf("Hello %s\n", "World");

This let you have as many consoles as you want.

ThreeStarProgrammer57
  • 2,906
  • 2
  • 16
  • 24