3

I try to redirect in windows the cmd.exe stdout&stdin (with CreateProcess()). It works fine as long as I run simple commands or open GUI apps, but if I try running a software like python, it does not give me it's output anymore (nor getting the input through stdin).

Code example:

#include <windows.h> 
#include <iostream>
#include <string>
#include <thread>

using namespace std;

HANDLE child_input_read;
HANDLE child_input_write;
HANDLE child_output_read;
HANDLE child_output_write;

void writeToPipe()
{
 while (true)
 {
  DWORD bytes_written;
  string msg;
  getline(cin, msg);
  msg += '\n';
  BOOL success = WriteFile(child_input_write, msg.c_str(), msg.size(), &bytes_written, NULL);
  if (!success)
  {
   break;
  }
 }
}
void readFromPipe()
{
 while (true)
 {
  DWORD bytes_read;
  char buffer[512];
  BOOL success = ReadFile(child_output_read, buffer, sizeof(buffer)-1, &bytes_read, NULL);
  buffer[bytes_read] = 0;
  if (!success)
  {
   break;
  }
  cout << buffer;
 }
}
void createCmdProcess()
{
 PROCESS_INFORMATION process_info;
 STARTUPINFO startup_info;
 SECURITY_ATTRIBUTES security_attributes;

 // Set the security attributes for the pipe handles created 
 security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES);
 security_attributes.bInheritHandle = TRUE;
 security_attributes.lpSecurityDescriptor = NULL;
 CreatePipe(&child_output_read, &child_output_write, &security_attributes, 0);
 CreatePipe(&child_input_read, &child_input_write, &security_attributes, 0);

 // Create the child process
 ZeroMemory(&process_info, sizeof(PROCESS_INFORMATION));
 ZeroMemory(&startup_info, sizeof(STARTUPINFO));
 startup_info.cb = sizeof(STARTUPINFO);
 startup_info.hStdInput = child_input_read;
 startup_info.hStdOutput = child_output_write;
 startup_info.hStdError = child_output_write;
 startup_info.dwFlags |= STARTF_USESTDHANDLES;
 CreateProcess(NULL, "cmd.exe", NULL, NULL, TRUE, 0, NULL, NULL, &startup_info, &process_info);
}

int main()
{
 createCmdProcess();
 thread t(writeToPipe);
 thread t2(readFromPipe);
 t.join();
 t2.join();
 system("pause");
}
Harry Johnston
  • 35,639
  • 6
  • 68
  • 158
liran63
  • 1,300
  • 2
  • 15
  • 17
  • 1
    By default, a console process gets the same stdin and stdout as its parent. However, there's no way to force a process to use the stdin and stdout that it's been given. I'd be surprised if Python misbehaved in this respect, though; are you sure you're running the console version? Where does the output actually wind up? – Harry Johnston Nov 22 '14 at 22:49
  • I'm sure I run the cmd.exe, but there is simply no output when I start python. – liran63 Nov 23 '14 at 05:31
  • If I start the cmd.exe from run and call python it works, but if I redirect cmd output it doesn't. I don't think the version of python has anything to do with the problem. – liran63 Nov 23 '14 at 15:10
  • Thank you Harry for your replies so far. I've edited my post and included the code (a sample which also doesn't work for me). It's compiled with visual c++ 2013, and running in win8.1. – liran63 Nov 24 '14 at 19:34
  • Yes I've just noticed that and edited it. It isn't the problem, I just quickly wrote a sample code. Sorry for that. – liran63 Nov 24 '14 at 19:58
  • I checked it again, and again same problem. I also checked it on windows 7 machine, and still, same problem. – liran63 Nov 24 '14 at 20:44
  • 1
    OK, I think I see what you mean; `cmd.exe` works, and so do Python commands, but Python's interactive mode doesn't. – Harry Johnston Nov 24 '14 at 20:48
  • I wrote a program which prints what it get's as input, and it works! So mabye there is really a problem only with python. That really weird to me, why should it be different? If I run python from run -> cmd.exe, it shows me the python command propmt and I execute python commands and etc. – liran63 Nov 24 '14 at 20:48

1 Answers1

3

It's not a bug, it's a feature. :-)

From Python Setup & Usage, section 1.1.1, Interface options (emphasis added):

The interpreter interface resembles that of the UNIX shell, but provides some additional methods of invocation:

When called with standard input connected to a tty device, it prompts for commands and executes them until an EOF (an end-of-file character, you can produce that with Ctrl-D on UNIX or Ctrl-Z, Enter on Windows) is read.

When called with a file name argument or with a file as standard input, it reads and executes a script from that file.

A pipe is neither a file nor a tty device, but it looks like a file as far as the C standard library (and hence Python) is concerned. So the second behaviour kicks in, and Python attempts to read to the end of file. Since we never close our end of the pipe, that never happens.

This behaviour isn't particularly sensible in my opinion (or at least not in Windows) and you could file a bug if you wanted. I would guess that such a proposal would be rejected, since any change would break backwards compatibility, but I could be wrong.

You can work around the problem by providing the -i option on the command line:

python -i

That makes Python enter interactive mode, despite the fact that stdin isn't a terminal.

Unfortunately, there is no known way to make a pipe that looks like a terminal on Windows.

Community
  • 1
  • 1
Harry Johnston
  • 35,639
  • 6
  • 68
  • 158