-1

I would like the program to be able to redirect input and output for both parent and child processes. For example,

parent "child.exe > child_output.txt" > parent_output.txt

Parent calls child. Child outputs to child_output, parent outputs to parent output.

The below example code should work, but doesn't. For convenience, I'm hard coding the child's redirection operator in the string "ToBeRun", rather than passing it as an argument, as will be done in the main program.

If no arguments are passed to the parent at the command line, the child output is properly redirected to child_output.txt. But, if you redirect the parent's argument on the command line, say by calling parent.exe > parent_output.txt, the parent output is not redirected. parent_output.txt is created, but is empty at program completion.

#include <windows.h>

// COPY PASTED FROM Martins Mozeiko on Handmade Network:
// https://hero.handmade.network/forums/code-discussion/t/94
// these shouldn't have to be in here, but hey! What can you do?
#pragma function(memset)
void *memset(void *dest, int c, size_t count)
{
    char *bytes = (char *)dest;
    while (count--)
    {
        *bytes++ = (char)c;
    }
    return dest;
}

#pragma function(memcpy)
void *memcpy(void *dest, void *src, size_t count)
{
    char *dest8 = (char *)dest;
    char *src8 = (char *)src;
    while (count--)
    {
        *dest8++ = *src8++;
    }
    return dest;
}
/* Based on this msdn article
 * https://msdn.microsoft.com/en-us/library/windows/desktop   /ms682512(v=vs.85).aspx
 */
int main(void)
{
    // init structures for child process
    STARTUPINFO si;
    PROCESS_INFORMATION pi;
    ZeroMemory( &si, sizeof(si) );
    si.cb = sizeof(si);
    ZeroMemory( &pi, sizeof(pi) );

    // start child process and wait for it to finish
#if 0
    char *ToBeRun = "/C child.exe > child_result.txt";
#else
    // for your convenience, so you don't have to compile a sample child.exe
    char *ToBeRun = "/C dir > child_result.txt";
#endif
    if( !CreateProcess("C:\\WINDOWS\\system32\\cmd.exe", // Module name
                       ToBeRun,     // Command line
                       NULL,        // Process handle not inheritable
                       NULL,        // Thread handle not inheritable
                       FALSE,       // Set handle inheritance to FALSE
                       0,           // No creation flags
                       NULL,        // Use parent's environment block
                       NULL,        // Use parent's starting directory 
                       &si,         // Pointer to STARTUPINFO structure
                       &pi)  // Pointer to PROCESS_INFORMATION structure
    ) 
    {
        // create process failed
        return 1;
    }
    WaitForSingleObject(pi.hProcess, INFINITE);

    // print parent output string
    char String[] = "This is the parent's output";
    int StringLength = sizeof(String) / sizeof(String[0]);
    DWORD CharsWritten;
    HANDLE Handle = GetStdHandle(STD_OUTPUT_HANDLE);
    WriteConsole(Handle, String, StringLength, &CharsWritten, NULL);

    // close process and thread handles. 
    CloseHandle( pi.hProcess );
    CloseHandle( pi.hThread );
    return 0;
}

// required for compiling without CRT
void mainCRTStartup(void)
{
    int Result = main();
    ExitProcess(Result);
}
Harry Johnston
  • 35,639
  • 6
  • 68
  • 158
  • 1
    Related: [How to measure execution time of command in windows command line?](http://stackoverflow.com/q/673523) – aschipfl Jan 29 '17 at 00:42
  • 1
    The kernel has nothing to do with the redirection operations, they are something `cmd.exe` does. I can see that your `ToBeRun` string is wrong; it should be something like `cmd /c text.exe > test_output.txt` but I'm not sure that's your problem. The redirection for the parent process should just work. You're going to need to show us a [mcve] for that one. – Harry Johnston Jan 29 '17 at 10:41
  • Thank you for your response. I'm sorry the post didn't meet the minimum requirements. It's been updated with a Minimal, Complete, and Verifiable example. – old_beginner Jan 29 '17 at 18:14
  • 1
    Please post your answer as an answer, not as part of your question. Note that you don't have to check whether it is a console handle, you can just use `WriteFile` which will work for console handles *and* file handles. (Although some programs do check so that they can produce different output, such as a progress bar or similar that doesn't make sense to write to a file.) – Harry Johnston Jan 30 '17 at 00:45
  • 1
    Also, strictly speaking `ToBeRun` should start `cmd /c` rather than just `/c`. By convention, the command line includes the module name as the first token. I believe that part of your code will work as written, but only because `cmd.exe` explicitly handles that case - it wouldn't work for most other executables. (Windows does not prepend the module name to the command line for you.) – Harry Johnston Jan 30 '17 at 00:49
  • Thank you very much Harry for your help and your patience! – old_beginner Jan 30 '17 at 02:50

1 Answers1

1

I answered my own question: simply put, WriteConsole() fails when writing to a redirected file.

Therefore, when writing to a redirected file, you need to use the aptly named WriteFile().

WriteFile() works with both redirected output and regular console output, as Harry Johnston has helpfully pointed out, so you can just use that for both cases. That's what I did.

Or, especially if you're working with Unicode, you can take MSDN's advice and check if the HANDLE is a file handle or console handle by calling GetConsoleMode. If GetConsoleMode returns 0, it's not a console.

Why would you want to do this? WriteConsole, unlike WriteFile, is a macro that can become a unicode or ASCII function, depending on whether UNICODE is defined.

From MSDN:

The ReadFile and WriteFile functions, or the ReadConsole and WriteConsole functions, enable an application to read console input and write console output as a stream of characters. ReadConsole and WriteConsole behave exactly like ReadFile and WriteFile except that they can be used either as wide-character functions (in which text arguments must use Unicode) or as ANSI functions (in which text arguments must use characters from the> Windows character set). Applications that need to maintain a single set of sources to support either Unicode or the ANSI character set should use ReadConsole and WriteConsole.

Community
  • 1
  • 1