25

I tried using CreateProcess to run a simple command like hg > test.txt. I tried running the string as a whole (as opposed to separating it into an application name and its parameters). Why does CreateProcess(0, "notepad.exe test.txt", ...) work but CreateProcess(0, "hg > test.txt", ...) does not?

Rob Kennedy
  • 161,384
  • 21
  • 275
  • 467
Paul Manta
  • 30,618
  • 31
  • 128
  • 208
  • 2
    `CreateProcess` doesn't know about redirections. Why don't you use `system(3)` ? Let me guess: you can do it better than system(3). – cnicutar Aug 10 '11 at 21:46
  • @cnicutar I've always wondered what the `function(number)` notation meant. What does `system(3)` mean? – Seth Carnegie Aug 10 '11 at 21:46
  • 2
    @Seth Carnegie The manual section. `man 3 system`. – cnicutar Aug 10 '11 at 21:47
  • 8
    `man 3 system` for Windows?? – Veger Aug 10 '11 at 21:48
  • 8
    @cnicutar he most definitely can do it better than system (on Windows). System will launch cmd.exe and load all kinds of dependencies and cause all kinds of unnecessary things to happen. – MK. Aug 10 '11 at 21:48
  • 14
    @cnicutar: Actually, [`CreateProcess()`](http://msdn.microsoft.com/en-us/library/ms682425(v=vs.85).aspx) has a `lpStartupInfo` structure that takes handles to the redirected streams, so it *does* know about redirections. It just doesn't use the shell notation. – André Caron Aug 10 '11 at 21:48
  • @Seth that's from Unix, you type in man 3 system and it means look up documentation for system in the section 3 of the manual pages (APIs, built-in commands etc) – MK. Aug 10 '11 at 21:49
  • @MK, André Caron Okay :-) I'm not sure if it's worth it in this case but I'll keep it in mind. – cnicutar Aug 10 '11 at 21:52

5 Answers5

32

The code below creates a console-less process with stdout and stderr redirected to the specified file.

#include <windows.h>


int _tmain(int argc, _TCHAR* argv[])
{
    SECURITY_ATTRIBUTES sa;
    sa.nLength = sizeof(sa);
    sa.lpSecurityDescriptor = NULL;
    sa.bInheritHandle = TRUE;       

    HANDLE h = CreateFile(_T("out.log"),
        FILE_APPEND_DATA,
        FILE_SHARE_WRITE | FILE_SHARE_READ,
        &sa,
        OPEN_ALWAYS,
        FILE_ATTRIBUTE_NORMAL,
        NULL );

    PROCESS_INFORMATION pi; 
    STARTUPINFO si;
    BOOL ret = FALSE; 
    DWORD flags = CREATE_NO_WINDOW;

    ZeroMemory( &pi, sizeof(PROCESS_INFORMATION) );
    ZeroMemory( &si, sizeof(STARTUPINFO) );
    si.cb = sizeof(STARTUPINFO); 
    si.dwFlags |= STARTF_USESTDHANDLES;
    si.hStdInput = NULL;
    si.hStdError = h;
    si.hStdOutput = h;

    TCHAR cmd[]= TEXT("Test.exe 30");
    ret = CreateProcess(NULL, cmd, NULL, NULL, TRUE, flags, NULL, NULL, &si, &pi);

    if ( ret ) 
    {
        CloseHandle(pi.hProcess);
        CloseHandle(pi.hThread);
        return 0;
    }

    return -1;
}
Dariusz
  • 21,561
  • 9
  • 74
  • 114
Lukas Koblovsky
  • 401
  • 5
  • 3
25

You can't use stdout redirection in the command line passed to CreateProcess. To redirect stdout you need to specify a file handle for the output in the STARTUPINFO structure.

You are also making another, more subtle, mistake. The second parameter, lpCommandLine must point to writeable memory because CreateProcess overwrites the buffer. If you happen to be using the ANSI version of the function then you will get away with this, but not for the Unicode version.

The Unicode version of this function, CreateProcessW, can modify the contents of this string. Therefore, this parameter cannot be a pointer to read-only memory (such as a const variable or a literal string). If this parameter is a constant string, the function may cause an access violation.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • He's not necessarily making a mistake by not making the second parameter point to writeable memory. It's possible that he doesn't compile with `UNICODE` defined, in which case `CreateProcess` is `CreateProcessA`, which doesn't have this requirement. – Frerich Raabe May 21 '12 at 15:52
  • @FrerichRaabe That's an implementation detail of the current versions of Windows. If you look at the MSDN documentation you will see that the type of the second parameter is `LPTSTR`. In any case, I made the very point that you make in my second paragraph. – David Heffernan May 21 '12 at 15:55
  • @DavidHeffernan: I don't think it's an implementation detail since the documentation you quoted explicitely mentions that the ANSI version does not have this requirement. I'm mostly nitpicking though, because you first wrote "You are also making a mistake" and then you wrote "If you happen to be using the ANSI version". So you don't *know* if he's doing a mistake, because you don't know whether he's the ANSI version or not. – Frerich Raabe May 21 '12 at 15:57
  • Related: [launch an exe/process with stdin stdout and stderr?](http://stackoverflow.com/questions/5485923/launch-an-exe-process-with-stdin-stdout-and-stderr/39648986). Also, see [tiny-process-library](https://github.com/eidheim/tiny-process-library) which is very convenient. – Delgan Sep 22 '16 at 21:30
10

Microsoft has an example how to redirect the standard output: http://msdn.microsoft.com/en-us/library/ms682499(VS.85).aspx.

Tamas Demjen
  • 690
  • 1
  • 6
  • 8
  • 11
    Yeah, that kind of work, but remember there is a bug in that example that prevents `ReadFromPipe` from exiting. Before you call that function you need to call: `CloseHandle(g_hChildStd_OUT_Wr);`. Also you'll probably want to wait for the child process to exit before you close the handle to process with something like: `WaitForSingleObject(piProcInfo.hProcess, INFINITE);`. – Nux Oct 26 '12 at 15:30
  • The example has since been fixed to call `CloseHandle(g_hChildStd_OUT_Wr); CloseHandle(g_hChildStd_IN_Rd);` – zett42 Oct 24 '22 at 10:01
8

CreateProcess() launches processes, it is not a command line itnerpreter. It doesn't know what ">" is and won't do the stream redirection for you. You need to open the file test.txt yourself and pass the handle to it to CreateProcess inside the STARTUPINFO structure: CreateProcess STARTUPINFO

MK.
  • 33,605
  • 18
  • 74
  • 111
0

you should run process cmd.exe with params "/c command line". This will redirect the output to a file or to organize a pipeline through CreateProcess.