tags:

views:

37

answers:

2

I have managed to get C code calling Python scripts happily on Unix using PIPES within the C code. I now need to do the same on Windows.

Essentially I would like to write scripts in different scripting languages like Python / Lua etc on Windows and be able to execute them using STDIN / STDOUT etc.

I have been looking at the "CreateProcess" call at:

http://msdn.microsoft.com/en-us/library/ms682425(VS.85).aspx

and although I can get it to work with a "child written in C", I cannot get it to call a Python script.

Below is the "parent / sender code" on my windows box:

#include<windows.h>
#include <stdio.h>
#include <stdlib.h>

#pragma comment(lib, "User32.lib")
void DisplayError(char *pszAPI);
void readFromPipe(HANDLE hPipeRead);
void createChildProcess(char *commandLine,
                        HANDLE hChildStdOut,
                        HANDLE hChildStdIn,
                        HANDLE hChildStdErr);
DWORD WINAPI writeToPipe(LPVOID lpvThreadParam);

HANDLE hChildProcess = NULL;
HANDLE hStdIn = NULL;
BOOL bRunThread = TRUE;
char *inputStream;

int main(int argc, char *argv[]){
  HANDLE hOutputReadTmp,hOutputRead,hOutputWrite;
  HANDLE hInputWriteTmp,hInputRead,hInputWrite;
  HANDLE hErrorWrite;
  HANDLE hThread;
  DWORD ThreadId;
  SECURITY_ATTRIBUTES sa;
  int streamLen;

  sa.nLength= sizeof(SECURITY_ATTRIBUTES);
  sa.lpSecurityDescriptor = NULL;
  sa.bInheritHandle = TRUE;

  if (!CreatePipe(&hOutputReadTmp,&hOutputWrite,&sa,0))
     return 1;

  if (!DuplicateHandle(GetCurrentProcess(),hOutputWrite,
                       GetCurrentProcess(),&hErrorWrite,0,
                       TRUE,DUPLICATE_SAME_ACCESS))
     return 1;

  if (!CreatePipe(&hInputRead,&hInputWriteTmp,&sa,0))
     return 1;

  if (!DuplicateHandle(GetCurrentProcess(),hOutputReadTmp,
                       GetCurrentProcess(),
                       &hOutputRead,
                       0,FALSE,
                       DUPLICATE_SAME_ACCESS))
     return 1;

  if (!DuplicateHandle(GetCurrentProcess(),hInputWriteTmp,
                       GetCurrentProcess(),
                       &hInputWrite,
                       0,FALSE,
                       DUPLICATE_SAME_ACCESS))
  return 1;

  if (!CloseHandle(hOutputReadTmp)) return 1;;
  if (!CloseHandle(hInputWriteTmp)) return 1;;

  if ( (hStdIn = GetStdHandle(STD_INPUT_HANDLE)) == INVALID_HANDLE_VALUE )
     return 1;

  if (argc == 2){
    createChildProcess(argv[1], hOutputWrite,hInputRead,hErrorWrite);
  }else{
    puts("No process name / input stream specified\n");
    return 1;
  }


  if (!CloseHandle(hOutputWrite)) return 1;;
  if (!CloseHandle(hInputRead )) return 1;;
  if (!CloseHandle(hErrorWrite)) return 1;;

  hThread = CreateThread(NULL,0,writeToPipe,
                          (LPVOID)hInputWrite,0,&ThreadId);
  if (hThread == NULL)
    return 1;;

  readFromPipe(hOutputRead);

  if (!CloseHandle(hStdIn))
     return 1;
  bRunThread = FALSE;

  if (WaitForSingleObject(hThread,INFINITE) == WAIT_FAILED)
     return 1;;

  if (!CloseHandle(hOutputRead)) return 1;;
  if (!CloseHandle(hInputWrite)) return 1;;
}

void createChildProcess(char *commandLine,
                        HANDLE hChildStdOut,
                        HANDLE hChildStdIn,
                        HANDLE hChildStdErr){
  PROCESS_INFORMATION pi;
  STARTUPINFO si;

  ZeroMemory(&si,sizeof(STARTUPINFO));
  si.cb = sizeof(STARTUPINFO);
  si.dwFlags = STARTF_USESTDHANDLES;
  si.hStdOutput = hChildStdOut;
  si.hStdInput  = hChildStdIn;
  si.hStdError  = hChildStdErr;

  if (!CreateProcess(NULL,commandLine,NULL,NULL,TRUE,
                     NULL,NULL,NULL,&si,&pi))
    hChildProcess = pi.hProcess;
  if (!CloseHandle(pi.hThread)) return 1;;
}

void readFromPipe(HANDLE hPipeRead)
{
  CHAR lpBuffer[256];
  DWORD nBytesRead;
  DWORD nCharsWritten;

  while(TRUE)
  {
     if (!ReadFile(hPipeRead,lpBuffer,sizeof(lpBuffer),
                                      &nBytesRead,NULL) || !nBytesRead)
     {
        if (GetLastError() == ERROR_BROKEN_PIPE)
           break; // pipe done - normal exit path.
        else
           return 1; // Something bad happened.
     }
     if (!WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE),lpBuffer,
                       nBytesRead,&nCharsWritten,NULL))
        return 1;;
  }
}

DWORD WINAPI writeToPipe(LPVOID lpvThreadParam)
{
  CHAR read_buff[256];
  DWORD nBytesRead,nBytesWrote;
  HANDLE hPipeWrite = (HANDLE)lpvThreadParam;

  while (bRunThread){
     nBytesRead = 21;
     strncpy(read_buff, "hello from the paren\n",21);
     read_buff[nBytesRead] = '\0';

     if (!WriteFile(hPipeWrite,read_buff,nBytesRead,&nBytesWrote,NULL)){
        if (GetLastError() == ERROR_NO_DATA)
           break; //Pipe was closed (normal exit path).
        else
        return 1;;
     }
  }
  return 1;
}

Quite a bit of the above code is "hardcoded" just for testing purposes...essentially I passing some text like "hello from the paren" to be sent to a "child.exe"....

Here is the code for the child.c...a simple ECHO of what is sent to it

#include<windows.h>
#include<stdio.h>
#include<string.h>

void main (){
    CHAR szInput[1024];
    ZeroMemory(szInput,1024);   
    gets(szInput);
    puts(szInput);
    fflush(NULL);   
}

To run the app I send "CallSubProcess.exe Child.exe" and it works 100%

Next I want to change "child.c" to be a PYTHON SCRIPT...

import sys

if __name__ == "__main__":
   inStream = sys.stdin.read()   
   outStream = inStream 
   sys.stdout.write(outStream)
   sys.stdout.flush()

So how can I change the CreateProcess call to execute this script?

if (!CreateProcess("C:\\Python26\\python.exe", "echo.py",NULL, NULL,FALSE, 0,NULL,NULL,&si,&pi)){

But it never works.

Any ideas how I can get this to work? Any help will be greatly appreciated.

+1  A: 

Maybe

if (!CreateProcess("C:\\Python26\\python.exe",
            "echo.py 'hello from parent'",
            NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) {
pmg
Hi there, thanks for the suggestion, but I want to send the stream into the PIPE rather than passing it to the Python script as a parameter? I have got the same thing working on Unix...just this CreateProcess seems to be giving major uphill for me ;-( Any other suggestions?
Lynton Grice
A: 

CreateProcess is kind of tricky to use.

From the MSDN documentation:

If both lpApplicationName and lpCommandLine are non-NULL, ... lpApplicationName specifies the module to execute, and ... lpCommandLine specifies the command line.... Console processes written in C can use the argc and argv arguments to parse the command line. Because argv[0] is the module name, C programmers generally repeat the module name as the first token in the command line.

To avoid the weirdness, I recommend always passing NULL for the first argument and to pass the full command-line as the second:

CreateProcess(NULL, "\"C:\\Python26\\python.exe\" echo.py", ...);
jamesdlin
Hi there, I tried your suggestion and still no luck ;-( Any other ideas?
Lynton Grice
@lyntongrice: I think I might have misunderstood your problem. Is it that you can't get the script to execute, or that you can't read its output?
jamesdlin