Handmade Network»Forums
3 posts
Need help badly redirecting input and output from command line on Windows
INTRO(SKIP ME) I'm a very new programmer and have been following Handmade Hero avidly for a few months. I'm writing a very simple timer utility, called from the command line, that can be used to benchmark batch files or small console executables. I wanted to give myself a challenge, so in keeping with the handmade spirit I wrote the thing without the C standard library (thanks Martinš Možeiko!). It turned out to be very difficult for me because I had to print PerfCount / PerfFreq. This involved weeks of trying to wrap my head around the algorithms for precisely printing floating point numbers (still an unsolved problem surprisingly enough!). I eventually just hacked it by multiplying the numerator by 100000000000 or so and using 128 bit integer division.

IMPORTANT PART
I would like the program to be able to redirect input and output for both
parent and child processes. For example,
1
timer "text.exe > test_output.txt" > timer_output.txt
should measure the time it takes for test.exe to output its data to test_output.txt and print that time measurement to timer_output.txt.

I managed to get the child process redirection working: remove the surrounding quotes, and pass it to CreateProcess in the lpCommandLine field. For example,
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
CommandLine = "timer \"text.exe > test_output.txt\" > timer_output.txt";
ToBeRun = "text.exe > test_output.txt";
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
)
{
    PrintString("CreateProcess failed\n");
    return 1;
}


But The parent process redirection is inscrutable. When I call GetCommandLine(), Windows has already stripped out any redirection operators. So that must mean they've already called SetStdHandle and redirected the input or output for me right? Well, it doesn't seem like it, because when I call GetStdHandle and WriteConsole in the parent process, it outputs to the console instead of the target file. But it gets weirder. If I use a redirection operator on the parent process, the child process handle is redirected, even though I have explicitly specified handle inheritance to be FALSE in CreateProcess(). I tried stepping through the outer calling processes in Visual Studio to find out where the redirection operators get cut out, but the key parts of the process seem to be in the hidden part of the kernel.

If you have any insights or suggestions or guesses or wild conspiratorial speculations about Microsoft's plans to drive all world's programmers except their own to insanity, please post them.

Sorry, this is probably too much of a beginner question for these forums, but stack overflow gave me no help and I've poured over MSDN articles for days to no avail, so I'm desperate.
Mārtiņš Možeiko
2562 posts / 2 projects
Need help badly redirecting input and output from command line on Windows
Edited by Mārtiņš Možeiko on
I'm not sure I understand the problem. I tried to run this code:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
#include <windows.h>
#include <stdio.h>

int main()
{
    STARTUPINFO si = {};
    PROCESS_INFORMATION pi;
    char ToBeRun[] = "/c text.exe > test_output.txt";
    if (!CreateProcess("C:\\WINDOWS\\system32\\cmd.exe", ToBeRun, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
    {
        printf("CreateProcess failed\n");
        return 1;
    }
    WaitForSingleObject(pi.hProcess, INFINITE);
    printf("Done\n");
}

I compiled it to timer.exe. Then I'm running it like this:
1
test.exe >timer_output.txt

And I see that "Done" is printed into "timer_output.txt" and whatever "text.exe" is printing goes to "test_output.txt" file.
Isn't this what you want?
3 posts
Need help badly redirecting input and output from command line on Windows
Thank you Mārtiņš!

Although you didn't understand my problem because I wasn't clear enough, you helped me solve it. You demonstrated that the CRT works fine with the redirecting parent process output to one file, and child process output to another, so I guessed my output function was the problem.

It turns out you can't use WriteConsole with a HANDLE to a file! That actually makes perfect sense. You have to call GetConsoleMode and check if it returns 0, and then call WriteConsole or WriteFile to output correctly. That makes less sense.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
    // print parent output string
    HANDLE Handle;
    char String[] = "This is the parent's result";
    int StringLength = sizeof(String) / sizeof(String[0]);
    DWORD CharsWritten;

    Handle = GetStdHandle(STD_OUTPUT_HANDLE);
    DWORD Ignored;
    bool32 IsConsole = GetConsoleMode(Handle, &Ignored);
    if(IsConsole) {
        WriteConsole(Handle, String, StringLength, &CharsWritten, NULL);
    }
    else { // it's a file
        WriteFile(Handle, String, StringLength - 1, &CharsWritten, NULL);
    }
Mārtiņš Možeiko
2562 posts / 2 projects
Need help badly redirecting input and output from command line on Windows
You can always use WriteFile to write to console. That means following program will work regardless of whether stdout is redirected to file or not:
1
2
3
4
5
6
7
8
9
#include <windows.h>

int main()
{
  HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);
  char str[] = "Hello World!\n";
  DWORD written;
  WriteFile(h, str, sizeof(str)-1, &written, NULL);
}
3 posts
Need help badly redirecting input and output from command line on Windows
Thank you again Mārtiņš! You've been so generous with your help!

I'm going to use your code instead because it's much simpler.

However, if anyone else cares, MSDN recommends testing with GetConsoleMode because WriteConsole is a macro with unicode support whereas WriteFile isn't.(Seems weird to me).

WriteConsole fails if it is used with a standard handle that is redirected to a file. If an application processes multilingual output that can be redirected, determine whether the output handle is a console handle (one method is to call the GetConsoleMode function and check whether it succeeds). If the handle is a console handle, call WriteConsole. If the handle is not a console handle, the output is redirected and you should call WriteFile to perform the I/O. Be sure to prefix a Unicode plain text file with a byte order mark. For more information, see Using Byte Order Marks.
Link.

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.
Link.

Just thought that might be helpful if anyone else on the internet ever has a similar problem.