www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - capturing process output on windows

reply "Carlos Santander B." <csantander619 gmail.com> writes:
What's the correct way of capturing a process output on Windows? I found 
a piece of code on the net, and I tried it, but since I haven't worked 
with pipes before, I don't know what's going on. Anyway, here's what I'm 
trying (using the core32 library):

//-------------------------------------------
//code found in
//http://www.experts-exchange.com/Programming/Programming_Languages/Delphi/Q_20682069.html

void main()
{
     char [] stdoutname=r"\\.\pipe\my_output_tmp",
         stderrname=r"\\.\pipe\my_error_tmp",
         stdinname=r"\\.\pipe\my_input_tmp";
     Stream fstderr, fstdout, fstdin;
     HANDLE hstderr, hstdout, hstdin;
     HANDLE outstreamhandle, errstreamhandle, instreamhandle;
     char * pOutputFile=toStringz(stdoutname), 
pInputFile=toStringz(stdinname);

     SECURITY_ATTRIBUTES SecAttrs;
     SecAttrs.nLength=SECURITY_ATTRIBUTES.sizeof;
     SecAttrs.bInheritHandle=true;

     outstreamhandle = CreateNamedPipeA(
         pOutputFile,
         PIPE_ACCESS_DUPLEX,
         PIPE_TYPE_BYTE | PIPE_WAIT,
         PIPE_UNLIMITED_INSTANCES,
         8192,
         8192,
         10000,
         &SecAttrs);

     assert(outstreamhandle!=INVALID_HANDLE_VALUE);

     hstdout = CreateFileA(
         pOutputFile,
         GENERIC_READ | GENERIC_WRITE,
         FILE_SHARE_READ | FILE_SHARE_WRITE,
         &SecAttrs,
         CREATE_ALWAYS,
         FILE_ATTRIBUTE_TEMPORARY,
         null );

     assert(hstdout!=INVALID_HANDLE_VALUE);

    instreamhandle= CreateNamedPipeA(
           pInputFile,
         PIPE_ACCESS_DUPLEX,
         PIPE_TYPE_BYTE | PIPE_WAIT,
         PIPE_UNLIMITED_INSTANCES,
         8192,
         8192,
         10000,
         &SecAttrs);

     assert(instreamhandle!=INVALID_HANDLE_VALUE);

     hstdin = CreateFileA(
          pInputFile,
          GENERIC_READ | GENERIC_WRITE,
          FILE_SHARE_READ | FILE_SHARE_WRITE,
          &SecAttrs,
          OPEN_ALWAYS,
          FILE_ATTRIBUTE_TEMPORARY,
          null );

     assert(hstdin!=INVALID_HANDLE_VALUE);

     STARTUPINFOA sui;
     sui.cb          = STARTUPINFOA.sizeof;
     sui.dwFlags     = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
     sui.wShowWindow = SW_HIDE;
     sui.hStdOutput  = hstdout;
     sui.hStdInput   = hstdin;

     PROCESS_INFORMATION pi;
     int res = CreateProcessA(null,
         "dir",
         null,
         null,
         true,
         CREATE_NEW_CONSOLE | REALTIME_PRIORITY_CLASS,
         null,
         null,
         &sui,
         &pi);

     DWORD size;
     size = GetFileSize(outstreamhandle, null);
     printf("%d\n", size);
     size = GetFileSize(hstdout, null);
     printf("%d\n", size);
     size = GetFileSize(instreamhandle, null);
     printf("%d\n", size);
     size = GetFileSize(hstdin, null);
     printf("%d\n", size);
     assert (size != INVALID_FILE_SIZE);

     /*
     fstdout=new BufferedFile(new File(cast(std.stream.HANDLE) 
outstreamhandle, FileMode.In | FileMode.Out ));
     foreach(char[] ln;fstdout)
         stdout.writeLine(ln);
     */

     /*
     fstdin=new BufferedFile(new File(cast(std.stream.HANDLE) 
instreamhandle, FileMode.In | FileMode.Out));
     foreach(char[] ln;fstdin)
         stdout.writeLine(ln);
     */
}
//-------------------------------------------

All asserts pass. All sizes return 0. Obviosly, even in an empty 
directory, "dir" outputs something.

Eventually, I'd like to do what's commented out.

BTW, I've never worked with pipes before on linux, but I also found some 
code to do the same, and it's unbelievably easy. When/if I get this 
Windows thing sorted out (a couple of days, maybe?) I'll release the 
code for anyone in need of it.

-- 
Carlos Santander Bernal

JP2, you'll always live in our minds
Apr 17 2005
next sibling parent reply "Andrew Fedoniouk" <news terrainformatica.com> writes:
It should not be so complicated....

Read this:
http://www.developersdomain.com/vb/articles/redirectpipe.htm
Or this:
http://support.microsoft.com/kb/q173085/

Funny, google returns VB links mostly.... 
Apr 17 2005
parent "Carlos Santander B." <csantander619 gmail.com> writes:
Andrew Fedoniouk wrote:
 It should not be so complicated....
 
 Read this:
 http://www.developersdomain.com/vb/articles/redirectpipe.htm
 Or this:
 http://support.microsoft.com/kb/q173085/
 
 Funny, google returns VB links mostly.... 
 
 

Thanks for that. It seems to work. Well, sort of, I still have a couple of things to check. -- Carlos Santander Bernal JP2, you'll always live in our minds
Apr 18 2005
prev sibling next sibling parent reply "Unknown W. Brackets" <unknown simplemachines.org> writes:
digitalmars.D/14470
news://news.digitalmars.com:119/cscp40$pee$1 digitaldaemon.com

I posted this earlier.  It's popen for Windows, which is probably that 
"unbelievably easy" method you mentioned.  Syntax is the same, or you 
cna use the ProcessStream implementation.

-[Unknown]


 What's the correct way of capturing a process output on Windows? I found 
 a piece of code on the net, and I tried it, but since I haven't worked 
 with pipes before, I don't know what's going on. Anyway, here's what I'm 
 trying (using the core32 library):

Apr 18 2005
parent "Carlos Santander B." <csantander619 gmail.com> writes:
Unknown W. Brackets wrote:
 digitalmars.D/14470
 news://news.digitalmars.com:119/cscp40$pee$1 digitaldaemon.com
 
 I posted this earlier.  It's popen for Windows, which is probably that 
 "unbelievably easy" method you mentioned.  Syntax is the same, or you 
 cna use the ProcessStream implementation.
 
 -[Unknown]
 

Thanks. However I'd like to do both reading and writing. What I have so far works that way on linux, and is kinda buggy on Windows. -- Carlos Santander Bernal JP2, you'll always live in our minds
Apr 18 2005
prev sibling next sibling parent "Carlos Santander B." <csantander619 gmail.com> writes:
Carlos Santander B. wrote:
 
 When/if I get this Windows thing sorted out (a couple of days,
 maybe?) I'll release the code for anyone in need of it.
 

It's not like it's really working on Windows, but I've uploaded it to dsource. It's in http://svn.dsource.org/svn/projects/walnut/downloads/some_other_things/ -- Carlos Santander Bernal JP2, you'll always live in our minds
Apr 18 2005
prev sibling next sibling parent David Medlock <nospam nospam.com> writes:
Carlos Santander B. wrote:
 What's the correct way of capturing a process output on Windows? I found 
 a piece of code on the net, and I tried it, but since I haven't worked 
 with pipes before, I don't know what's going on. Anyway, here's what I'm 
 trying (using the core32 library):
 
 //-------------------------------------------
 //code found in
 //http://www.experts-exchange.com/Programming/Programming_Languages/De
phi/Q_20682069.html 
 
 
 void main()
 {
     char [] stdoutname=r"\\.\pipe\my_output_tmp",
         stderrname=r"\\.\pipe\my_error_tmp",
         stdinname=r"\\.\pipe\my_input_tmp";
     Stream fstderr, fstdout, fstdin;
     HANDLE hstderr, hstdout, hstdin;
     HANDLE outstreamhandle, errstreamhandle, instreamhandle;
     char * pOutputFile=toStringz(stdoutname), 
 pInputFile=toStringz(stdinname);
 
     SECURITY_ATTRIBUTES SecAttrs;
     SecAttrs.nLength=SECURITY_ATTRIBUTES.sizeof;
     SecAttrs.bInheritHandle=true;
 
     outstreamhandle = CreateNamedPipeA(
         pOutputFile,
         PIPE_ACCESS_DUPLEX,
         PIPE_TYPE_BYTE | PIPE_WAIT,
         PIPE_UNLIMITED_INSTANCES,
         8192,
         8192,
         10000,
         &SecAttrs);
 
     assert(outstreamhandle!=INVALID_HANDLE_VALUE);
 
     hstdout = CreateFileA(
         pOutputFile,
         GENERIC_READ | GENERIC_WRITE,
         FILE_SHARE_READ | FILE_SHARE_WRITE,
         &SecAttrs,
         CREATE_ALWAYS,
         FILE_ATTRIBUTE_TEMPORARY,
         null );
 
     assert(hstdout!=INVALID_HANDLE_VALUE);
 
    instreamhandle= CreateNamedPipeA(
           pInputFile,
         PIPE_ACCESS_DUPLEX,
         PIPE_TYPE_BYTE | PIPE_WAIT,
         PIPE_UNLIMITED_INSTANCES,
         8192,
         8192,
         10000,
         &SecAttrs);
 
     assert(instreamhandle!=INVALID_HANDLE_VALUE);
 
     hstdin = CreateFileA(
          pInputFile,
          GENERIC_READ | GENERIC_WRITE,
          FILE_SHARE_READ | FILE_SHARE_WRITE,
          &SecAttrs,
          OPEN_ALWAYS,
          FILE_ATTRIBUTE_TEMPORARY,
          null );
 
     assert(hstdin!=INVALID_HANDLE_VALUE);
 
     STARTUPINFOA sui;
     sui.cb          = STARTUPINFOA.sizeof;
     sui.dwFlags     = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
     sui.wShowWindow = SW_HIDE;
     sui.hStdOutput  = hstdout;
     sui.hStdInput   = hstdin;
 
     PROCESS_INFORMATION pi;
     int res = CreateProcessA(null,
         "dir",
         null,
         null,
         true,
         CREATE_NEW_CONSOLE | REALTIME_PRIORITY_CLASS,
         null,
         null,
         &sui,
         &pi);
 
     DWORD size;
     size = GetFileSize(outstreamhandle, null);
     printf("%d\n", size);
     size = GetFileSize(hstdout, null);
     printf("%d\n", size);
     size = GetFileSize(instreamhandle, null);
     printf("%d\n", size);
     size = GetFileSize(hstdin, null);
     printf("%d\n", size);
     assert (size != INVALID_FILE_SIZE);
 
     /*
     fstdout=new BufferedFile(new File(cast(std.stream.HANDLE) 
 outstreamhandle, FileMode.In | FileMode.Out ));
     foreach(char[] ln;fstdout)
         stdout.writeLine(ln);
     */
 
     /*
     fstdin=new BufferedFile(new File(cast(std.stream.HANDLE) 
 instreamhandle, FileMode.In | FileMode.Out));
     foreach(char[] ln;fstdin)
         stdout.writeLine(ln);
     */
 }
 //-------------------------------------------
 
 All asserts pass. All sizes return 0. Obviosly, even in an empty 
 directory, "dir" outputs something.
 
 Eventually, I'd like to do what's commented out.
 
 BTW, I've never worked with pipes before on linux, but I also found some 
 code to do the same, and it's unbelievably easy. When/if I get this 
 Windows thing sorted out (a couple of days, maybe?) I'll release the 
 code for anyone in need of it.
 

You can get the source to DManager, which is in Delphi but uses the Win32 API to capture output. Look in MyUtils.pas -David
Apr 18 2005
prev sibling parent reply "Regan Heath" <regan netwin.co.nz> writes:
I have some Win32 C code for doing just this.. I reckon I can whip it into  
a stream class or similar, give me a few days...

Regan

On Sun, 17 Apr 2005 17:01:07 -0500, Carlos Santander B.  
<csantander619 gmail.com> wrote:

 What's the correct way of capturing a process output on Windows? I found  
 a piece of code on the net, and I tried it, but since I haven't worked  
 with pipes before, I don't know what's going on. Anyway, here's what I'm  
 trying (using the core32 library):

 //-------------------------------------------
 //code found in
 //http://www.experts-exchange.com/Programming/Programming_Languages/Delphi/Q_20682069.html

 void main()
 {
      char [] stdoutname=r"\\.\pipe\my_output_tmp",
          stderrname=r"\\.\pipe\my_error_tmp",
          stdinname=r"\\.\pipe\my_input_tmp";
      Stream fstderr, fstdout, fstdin;
      HANDLE hstderr, hstdout, hstdin;
      HANDLE outstreamhandle, errstreamhandle, instreamhandle;
      char * pOutputFile=toStringz(stdoutname),  
 pInputFile=toStringz(stdinname);

      SECURITY_ATTRIBUTES SecAttrs;
      SecAttrs.nLength=SECURITY_ATTRIBUTES.sizeof;
      SecAttrs.bInheritHandle=true;

      outstreamhandle = CreateNamedPipeA(
          pOutputFile,
          PIPE_ACCESS_DUPLEX,
          PIPE_TYPE_BYTE | PIPE_WAIT,
          PIPE_UNLIMITED_INSTANCES,
          8192,
          8192,
          10000,
          &SecAttrs);

      assert(outstreamhandle!=INVALID_HANDLE_VALUE);

      hstdout = CreateFileA(
          pOutputFile,
          GENERIC_READ | GENERIC_WRITE,
          FILE_SHARE_READ | FILE_SHARE_WRITE,
          &SecAttrs,
          CREATE_ALWAYS,
          FILE_ATTRIBUTE_TEMPORARY,
          null );

      assert(hstdout!=INVALID_HANDLE_VALUE);

     instreamhandle= CreateNamedPipeA(
            pInputFile,
          PIPE_ACCESS_DUPLEX,
          PIPE_TYPE_BYTE | PIPE_WAIT,
          PIPE_UNLIMITED_INSTANCES,
          8192,
          8192,
          10000,
          &SecAttrs);

      assert(instreamhandle!=INVALID_HANDLE_VALUE);

      hstdin = CreateFileA(
           pInputFile,
           GENERIC_READ | GENERIC_WRITE,
           FILE_SHARE_READ | FILE_SHARE_WRITE,
           &SecAttrs,
           OPEN_ALWAYS,
           FILE_ATTRIBUTE_TEMPORARY,
           null );

      assert(hstdin!=INVALID_HANDLE_VALUE);

      STARTUPINFOA sui;
      sui.cb          = STARTUPINFOA.sizeof;
      sui.dwFlags     = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
      sui.wShowWindow = SW_HIDE;
      sui.hStdOutput  = hstdout;
      sui.hStdInput   = hstdin;

      PROCESS_INFORMATION pi;
      int res = CreateProcessA(null,
          "dir",
          null,
          null,
          true,
          CREATE_NEW_CONSOLE | REALTIME_PRIORITY_CLASS,
          null,
          null,
          &sui,
          &pi);

      DWORD size;
      size = GetFileSize(outstreamhandle, null);
      printf("%d\n", size);
      size = GetFileSize(hstdout, null);
      printf("%d\n", size);
      size = GetFileSize(instreamhandle, null);
      printf("%d\n", size);
      size = GetFileSize(hstdin, null);
      printf("%d\n", size);
      assert (size != INVALID_FILE_SIZE);

      /*
      fstdout=new BufferedFile(new File(cast(std.stream.HANDLE)  
 outstreamhandle, FileMode.In | FileMode.Out ));
      foreach(char[] ln;fstdout)
          stdout.writeLine(ln);
      */

      /*
      fstdin=new BufferedFile(new File(cast(std.stream.HANDLE)  
 instreamhandle, FileMode.In | FileMode.Out));
      foreach(char[] ln;fstdin)
          stdout.writeLine(ln);
      */
 }
 //-------------------------------------------

 All asserts pass. All sizes return 0. Obviosly, even in an empty  
 directory, "dir" outputs something.

 Eventually, I'd like to do what's commented out.

 BTW, I've never worked with pipes before on linux, but I also found some  
 code to do the same, and it's unbelievably easy. When/if I get this  
 Windows thing sorted out (a couple of days, maybe?) I'll release the  
 code for anyone in need of it.

Apr 18 2005
parent reply "Regan Heath" <regan netwin.co.nz> writes:
------------SEKiDH0RfyFOL1Flz3esMn
Content-Type: text/plain; format=flowed; delsp=yes; charset=iso-8859-15
Content-Transfer-Encoding: 8bit

As promised... Tho it should be noted that you cannot simply run "dir"  
with this. it wants an actual executable not a cmd.exe command like "dir".  
However you can run "cmd /c dir" and get a directory listing.

It would be nice if Ben had time to look at it and make sure I have not  
abused Stream ;)

Regan

On Tue, 19 Apr 2005 16:40:55 +1200, Regan Heath <regan netwin.co.nz> wrote:
 I have some Win32 C code for doing just this.. I reckon I can whip it  
 into a stream class or similar, give me a few days...

 Regan

 On Sun, 17 Apr 2005 17:01:07 -0500, Carlos Santander B.  
 <csantander619 gmail.com> wrote:

 What's the correct way of capturing a process output on Windows? I  
 found a piece of code on the net, and I tried it, but since I haven't  
 worked with pipes before, I don't know what's going on. Anyway, here's  
 what I'm trying (using the core32 library):

 //-------------------------------------------
 //code found in
 //http://www.experts-exchange.com/Programming/Programming_Languages/Delphi/Q_20682069.html

 void main()
 {
      char [] stdoutname=r"\\.\pipe\my_output_tmp",
          stderrname=r"\\.\pipe\my_error_tmp",
          stdinname=r"\\.\pipe\my_input_tmp";
      Stream fstderr, fstdout, fstdin;
      HANDLE hstderr, hstdout, hstdin;
      HANDLE outstreamhandle, errstreamhandle, instreamhandle;
      char * pOutputFile=toStringz(stdoutname),  
 pInputFile=toStringz(stdinname);

      SECURITY_ATTRIBUTES SecAttrs;
      SecAttrs.nLength=SECURITY_ATTRIBUTES.sizeof;
      SecAttrs.bInheritHandle=true;

      outstreamhandle = CreateNamedPipeA(
          pOutputFile,
          PIPE_ACCESS_DUPLEX,
          PIPE_TYPE_BYTE | PIPE_WAIT,
          PIPE_UNLIMITED_INSTANCES,
          8192,
          8192,
          10000,
          &SecAttrs);

      assert(outstreamhandle!=INVALID_HANDLE_VALUE);

      hstdout = CreateFileA(
          pOutputFile,
          GENERIC_READ | GENERIC_WRITE,
          FILE_SHARE_READ | FILE_SHARE_WRITE,
          &SecAttrs,
          CREATE_ALWAYS,
          FILE_ATTRIBUTE_TEMPORARY,
          null );

      assert(hstdout!=INVALID_HANDLE_VALUE);

     instreamhandle= CreateNamedPipeA(
            pInputFile,
          PIPE_ACCESS_DUPLEX,
          PIPE_TYPE_BYTE | PIPE_WAIT,
          PIPE_UNLIMITED_INSTANCES,
          8192,
          8192,
          10000,
          &SecAttrs);

      assert(instreamhandle!=INVALID_HANDLE_VALUE);

      hstdin = CreateFileA(
           pInputFile,
           GENERIC_READ | GENERIC_WRITE,
           FILE_SHARE_READ | FILE_SHARE_WRITE,
           &SecAttrs,
           OPEN_ALWAYS,
           FILE_ATTRIBUTE_TEMPORARY,
           null );

      assert(hstdin!=INVALID_HANDLE_VALUE);

      STARTUPINFOA sui;
      sui.cb          = STARTUPINFOA.sizeof;
      sui.dwFlags     = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
      sui.wShowWindow = SW_HIDE;
      sui.hStdOutput  = hstdout;
      sui.hStdInput   = hstdin;

      PROCESS_INFORMATION pi;
      int res = CreateProcessA(null,
          "dir",
          null,
          null,
          true,
          CREATE_NEW_CONSOLE | REALTIME_PRIORITY_CLASS,
          null,
          null,
          &sui,
          &pi);

      DWORD size;
      size = GetFileSize(outstreamhandle, null);
      printf("%d\n", size);
      size = GetFileSize(hstdout, null);
      printf("%d\n", size);
      size = GetFileSize(instreamhandle, null);
      printf("%d\n", size);
      size = GetFileSize(hstdin, null);
      printf("%d\n", size);
      assert (size != INVALID_FILE_SIZE);

      /*
      fstdout=new BufferedFile(new File(cast(std.stream.HANDLE)  
 outstreamhandle, FileMode.In | FileMode.Out ));
      foreach(char[] ln;fstdout)
          stdout.writeLine(ln);
      */

      /*
      fstdin=new BufferedFile(new File(cast(std.stream.HANDLE)  
 instreamhandle, FileMode.In | FileMode.Out));
      foreach(char[] ln;fstdin)
          stdout.writeLine(ln);
      */
 }
 //-------------------------------------------

 All asserts pass. All sizes return 0. Obviosly, even in an empty  
 directory, "dir" outputs something.

 Eventually, I'd like to do what's commented out.

 BTW, I've never worked with pipes before on linux, but I also found  
 some code to do the same, and it's unbelievably easy. When/if I get  
 this Windows thing sorted out (a couple of days, maybe?) I'll release  
 the code for anyone in need of it.


------------SEKiDH0RfyFOL1Flz3esMn Content-Disposition: attachment; filename=process.d Content-Type: application/octet-stream; name=process.d Content-Transfer-Encoding: 8bit /* * Copyright (c) 2005 * Regan Heath * * Permission to use, copy, modify, distribute and sell this software * and its documentation for any purpose is hereby granted without fee, * provided that the above copyright notice appear in all copies and * that both that copyright notice and this permission notice appear * in supporting documentation. Author makes no representations about * the suitability of this software for any purpose. It is provided * "as is" without express or implied warranty. */ module lib.process; import std.stream; version(Windows) { import std.c.windows.windows; import std.windows.syserror; extern(Windows) { struct PROCESS_INFORMATION { HANDLE hProcess; HANDLE hThread; DWORD dwProcessId; DWORD dwThreadId; } alias PROCESS_INFORMATION* PPROCESS_INFORMATION, LPPROCESS_INFORMATION; alias HANDLE* PHANDLE; BOOL CreatePipe(PHANDLE hReadPipe, PHANDLE hWritePipe, LPSECURITY_ATTRIBUTES lpPipeAttributes, DWORD nSize); struct STARTUPINFOA { DWORD cb; LPSTR lpReserved; LPSTR lpDesktop; LPSTR lpTitle; DWORD dwX; DWORD dwY; DWORD dwXSize; DWORD dwYSize; DWORD dwXCountChars; DWORD dwYCountChars; DWORD dwFillAttribute; DWORD dwFlags; WORD wShowWindow; WORD cbReserved2; LPBYTE lpReserved2; HANDLE hStdInput; HANDLE hStdOutput; HANDLE hStdError; } alias STARTUPINFOA* LPSTARTUPINFOA; struct STARTUPINFOW { DWORD cb; LPWSTR lpReserved; LPWSTR lpDesktop; LPWSTR lpTitle; DWORD dwX; DWORD dwY; DWORD dwXSize; DWORD dwYSize; DWORD dwXCountChars; DWORD dwYCountChars; DWORD dwFillAttribute; DWORD dwFlags; WORD wShowWindow; WORD cbReserved2; LPBYTE lpReserved2; HANDLE hStdInput; HANDLE hStdOutput; HANDLE hStdError; } alias STARTUPINFOW* LPSTARTUPINFOW; VOID GetStartupInfoA(LPSTARTUPINFOA lpStartupInfo); VOID GetStartupInfoW(LPSTARTUPINFOW lpStartupInfo); uint STARTF_USESHOWWINDOW = 0x00000001; uint STARTF_USESIZE = 0x00000002; uint STARTF_USEPOSITION = 0x00000004; uint STARTF_USECOUNTCHARS = 0x00000008; uint STARTF_USEFILLATTRIBUTE = 0x00000010; uint STARTF_RUNFULLSCREEN = 0x00000020; uint STARTF_FORCEONFEEDBACK = 0x00000040; uint STARTF_FORCEOFFFEEDBACK = 0x00000080; uint STARTF_USESTDHANDLES = 0x00000100; /+#if(WINVER >= 0x0400) #define STARTF_USEHOTKEY 0x00000200 #endif /* WINVER >= 0x0400 */ +/ BOOL CreateProcessA( LPCSTR lpApplicationName, LPSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCSTR lpCurrentDirectory, LPSTARTUPINFOA lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation ); BOOL CreateProcessW( LPCWSTR lpApplicationName, LPWSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCWSTR lpCurrentDirectory, LPSTARTUPINFOW lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation ); // // dwCreationFlag values // uint DEBUG_PROCESS = 0x00000001; uint DEBUG_ONLY_THIS_PROCESS = 0x00000002; uint CREATE_SUSPENDED = 0x00000004; uint DETACHED_PROCESS = 0x00000008; uint CREATE_NEW_CONSOLE = 0x00000010; uint NORMAL_PRIORITY_CLASS = 0x00000020; uint IDLE_PRIORITY_CLASS = 0x00000040; uint HIGH_PRIORITY_CLASS = 0x00000080; uint REALTIME_PRIORITY_CLASS = 0x00000100; uint CREATE_NEW_PROCESS_GROUP = 0x00000200; uint CREATE_UNICODE_ENVIRONMENT = 0x00000400; uint CREATE_SEPARATE_WOW_VDM = 0x00000800; uint CREATE_SHARED_WOW_VDM = 0x00001000; uint CREATE_FORCEDOS = 0x00002000; uint CREATE_DEFAULT_ERROR_MODE = 0x04000000; uint CREATE_NO_WINDOW = 0x08000000; uint PROFILE_USER = 0x10000000; uint PROFILE_KERNEL = 0x20000000; uint PROFILE_SERVER = 0x40000000; BOOL TerminateProcess(HANDLE hProcess, UINT uExitCode); BOOL PeekNamedPipe( HANDLE hNamedPipe, LPVOID lpBuffer, DWORD nBufferSize, LPDWORD lpBytesRead, LPDWORD lpTotalBytesAvail, LPDWORD lpBytesLeftThisMessage ); } } extern(C) char* strdup(char*); version(Windows) { class ProcessException : Exception { this(char[] msg) { super(msg ~ ": " ~ sysErrorString(GetLastError())); } } } version(linux) { private import std.c.stdlib; extern (C) char* strerror(int); class ProcessException : Exception { //for some reason getErrno does not link for me? this(char[] msg) { super(msg ~ ": " ~ std.string.toString(strerror(getErrno()))); } } } class Process : Stream { alias std.stdio.writefln debugf; this() { super(); seekable = false; readable = true; writeable = true; isopen = false; } this(char[] command) { this(); open(command); } ~this() { close(); } void addEnv(char[] label, char[] value) { addEnv(label~"="~value); } void addEnv(char[] value) { enviroment ~= value; } void open(char[] command) { if (isopen) close(); startProcess(command); } override void close() { if (!isopen) return ; flush(); stopProcess(0); isopen = false; } override ulong seek(long offset, SeekPos whence) { assertSeekable(); } version(Windows) { override size_t readBlock(void* buffer, size_t size) { size_t bytes = 0; if (!isopen) return 0; if (!ReadFile(output,buffer,size,&bytes,null)) throw new ProcessException("ReadFile"); return bytes; } override size_t writeBlock(void* buffer, size_t size) { size_t bytes = 0; if (!isopen) return 0; if (!WriteFile(input,buffer,size,&bytes,null)) throw new ProcessException("WriteFile"); return bytes; } override size_t available() { size_t bytes = 0; if (!isopen) return 0; if (!PeekNamedPipe(output,null,0,null,&bytes,null)) throw new ProcessException("PeekNamedPipe"); return bytes; } override void flush() { if (!isopen) return ; FlushFileBuffers(output); } } version(linux) { override size_t readBlock(void* buffer, size_t size) { size_t bytes; bytes = read(output,buffer,size); if (bytes == -1) throw new ProcessException("read"); return bytes; } override size_t writeBlock(void* buffer, size_t size) { size_t bytes; bytes = write(output,buffer,size); if (bytes == -1) throw new ProcessException("write"); return bytes; } override size_t available() { size_t bytes; if (ioctl(output,FIONREAD,&bytes) == -1) throw new ProcessException("ioctl"); return bytes; } } private: char[][] enviroment = null; version(Windows) { HANDLE output = INVALID_HANDLE_VALUE; HANDLE input = INVALID_HANDLE_VALUE; PROCESS_INFORMATION* info = null; int bufferSize = 0; char* makeBlock(char[][] from) { char* result = null; uint length = 0; uint upto = 0; foreach(char[] s; from) length += s.length; //total length of strings length += from.length; //add space for a \0 for each string length++; //add space for final terminating \0 result = cast(char*)calloc(0,length); foreach(char[] s; from) { result[upto..upto+s.length] = s[0..s.length]; upto += s.length+1; } return result; } void freeBlock(char* data) { free(data); } void startProcess(char[] command) { HANDLE read1,read2,read3,write1,write2,write3; SECURITY_ATTRIBUTES security; STARTUPINFOA startup; char* env; read1 = read2 = read3 = write1 = write2 = write2 = INVALID_HANDLE_VALUE; security.nLength = security.sizeof; security.lpSecurityDescriptor = null; security.bInheritHandle = true; env = null; try { if (!CreatePipe(&read1,&write1,&security,bufferSize)) throw new ProcessException("CreatePipe"); if (!CreatePipe(&read2,&write2,&security,bufferSize)) throw new ProcessException("CreatePipe"); if (!CreatePipe(&read3,&write3,&security,bufferSize)) throw new ProcessException("CreatePipe"); GetStartupInfoA(&startup); startup.hStdInput = read1; startup.hStdOutput = write2; startup.hStdError = write3; startup.dwFlags = STARTF_USESTDHANDLES; info = new PROCESS_INFORMATION(); env = makeBlock(enviroment); if (!CreateProcessA(null,toStringz(command),null,null,true,DETACHED_PROCESS,env,null,&startup,info)) throw new ProcessException("CreateProcess"); input = write1; output = read2; isopen = true; } finally { if (!isopen && read2 != INVALID_HANDLE_VALUE) CloseHandle(read2); if (!isopen && write1 != INVALID_HANDLE_VALUE) CloseHandle(write1); if (read1 != INVALID_HANDLE_VALUE) CloseHandle(read1); if (read3 != INVALID_HANDLE_VALUE) CloseHandle(read3); if (write2 != INVALID_HANDLE_VALUE) CloseHandle(write2); if (write3 != INVALID_HANDLE_VALUE) CloseHandle(write3); if (info) CloseHandle(info.hThread); if (env) freeBlock(env); } } void stopProcess(uint exitCode) { if (!TerminateProcess(info.hProcess,exitCode)) throw new ProcessException("TerminateProcess"); isopen = false; CloseHandle(info.hProcess); delete info; info = null; CloseHandle(output); output = INVALID_HANDLE_VALUE; CloseHandle(input); input = INVALID_HANDLE_VALUE; } } version(linux) { int output = 0; int input = 0; int pid = 0; char** makeBlock(char[][] from) { char** result = null; result = cast(char**)calloc(0,(enviroment.length+1) * typeid(char*).sizeof); foreach(uint i, char[] s; from) result[i] = strdup(toStringz(s)); return result; } void freeBlock(char** block) { for(uint i = 0; block[i]; i++) free(block[i]); free(block); } char[][] splitArgs(char[] string, char[] delims) { char[] delims = " \t\r\n"; char[][] results = null; bool isquot = false; int start = -1; for(int i = 0; i < string.length; i++) { if (string[i] == '\"') isquot = !isquot; if (isquot) continue; if (delims.find(string[i]) != -1) { if (start == -1) continue; results ~= string[start..i]; start = -1; continue; } if (start == -1) start = i; } return results; } void startProcess(char[] command) { int xwrite[2]; int xread[2]; try { if (pipe(xwrite) == -1) throw new ProcessException("pipe(xwrite)"); if (pipe(xread) == -1) throw new ProcessException("pipe(xread)"); if (fcntl(input, F_SETFD, 1) == -1) throw new ProcessException("fcntl(input)"); if (fcntl(output, F_SETFD, 1) == -1) throw new ProcessException("fcntl(output)"); if (fcntl(fileno(stdin), F_SETFD, 1) == -1) throw new ProcessException("fcntl(stdin)"); if (fcntl(fileno(stdout), F_SETFD, 1) == -1) throw new ProcessException("fcntl(stdout)"); if (fcntl(fileno(stderr), F_SETFD, 1) == -1) throw new ProcessException("fcntl(stderr)"); pid = fork(); if (pid == 0) { /* child */ char** args = null; char** env = null; //not sure if we can even throw here? if (dup2(xwrite[1],STDOUT_FILENO) == -1) {} //throw new ProcessException("dup2(xwrite[1])"); if (dup2(xread[0], STDIN_FILENO) == -1) {} //throw new ProcessException("dup2(xread[0])"); close(xwrite[1]); close(xread[0]); /* set child uid/gid here */ //if (setuid(uid) == -1) throw new ProcessException("setuid"); //if (setgid(gid) == -1) throw new ProcessException("setgid"); args = makeBlock(splitArgs(command)); env = makeBlock(enviroment); execve(args[0],args,env); //this does not return on success //can we throw? how to notify parent of failure? exit(1); } /* parent */ isopen = true; } finally { close(xwrite[1]); close(xread[0]); } } void stopProcess(uint dummy) { int r; if (pid == 0) return; if (kill(pid, SIGTERM) == -1) throw new ProcessException("kill"); for(uint i = 0; i < 100; i++) { r = waitpid(pid,null,WNOHANG|WUNTRACED); if (r == -1) throw new ProcessException("waitpid"); if (r == pid) break; usleep(50000); } isopen = false; close(output); close(input); pid = 0; } } } /+ void main() { auto Process p = new Process("cmd /c dir"); printf("%.*s",p.readLine()); } +/ ------------SEKiDH0RfyFOL1Flz3esMn--
Apr 19 2005
next sibling parent "Regan Heath" <regan netwin.co.nz> writes:
Err.. I forgot to mention the linux version of this code is _totally_  
untested, in fact I haven't even compiled it.

On Wed, 20 Apr 2005 01:06:15 +1200, Regan Heath <regan netwin.co.nz> wrote:
 As promised... Tho it should be noted that you cannot simply run "dir"
 with this. it wants an actual executable not a cmd.exe command like  
 "dir".
 However you can run "cmd /c dir" and get a directory listing.

 It would be nice if Ben had time to look at it and make sure I have not
 abused Stream ;)

 Regan

 On Tue, 19 Apr 2005 16:40:55 +1200, Regan Heath <regan netwin.co.nz>  
 wrote:
 I have some Win32 C code for doing just this.. I reckon I can whip it
 into a stream class or similar, give me a few days...

 Regan

 On Sun, 17 Apr 2005 17:01:07 -0500, Carlos Santander B.
 <csantander619 gmail.com> wrote:

 What's the correct way of capturing a process output on Windows? I
 found a piece of code on the net, and I tried it, but since I haven't
 worked with pipes before, I don't know what's going on. Anyway, here's
 what I'm trying (using the core32 library):

 //-------------------------------------------
 //code found in
 //http://www.experts-exchange.com/Programming/Programming_Languages/Delphi/Q_20682069.html

 void main()
 {
      char [] stdoutname=r"\\.\pipe\my_output_tmp",
          stderrname=r"\\.\pipe\my_error_tmp",
          stdinname=r"\\.\pipe\my_input_tmp";
      Stream fstderr, fstdout, fstdin;
      HANDLE hstderr, hstdout, hstdin;
      HANDLE outstreamhandle, errstreamhandle, instreamhandle;
      char * pOutputFile=toStringz(stdoutname),
 pInputFile=toStringz(stdinname);

      SECURITY_ATTRIBUTES SecAttrs;
      SecAttrs.nLength=SECURITY_ATTRIBUTES.sizeof;
      SecAttrs.bInheritHandle=true;

      outstreamhandle = CreateNamedPipeA(
          pOutputFile,
          PIPE_ACCESS_DUPLEX,
          PIPE_TYPE_BYTE | PIPE_WAIT,
          PIPE_UNLIMITED_INSTANCES,
          8192,
          8192,
          10000,
          &SecAttrs);

      assert(outstreamhandle!=INVALID_HANDLE_VALUE);

      hstdout = CreateFileA(
          pOutputFile,
          GENERIC_READ | GENERIC_WRITE,
          FILE_SHARE_READ | FILE_SHARE_WRITE,
          &SecAttrs,
          CREATE_ALWAYS,
          FILE_ATTRIBUTE_TEMPORARY,
          null );

      assert(hstdout!=INVALID_HANDLE_VALUE);

     instreamhandle= CreateNamedPipeA(
            pInputFile,
          PIPE_ACCESS_DUPLEX,
          PIPE_TYPE_BYTE | PIPE_WAIT,
          PIPE_UNLIMITED_INSTANCES,
          8192,
          8192,
          10000,
          &SecAttrs);

      assert(instreamhandle!=INVALID_HANDLE_VALUE);

      hstdin = CreateFileA(
           pInputFile,
           GENERIC_READ | GENERIC_WRITE,
           FILE_SHARE_READ | FILE_SHARE_WRITE,
           &SecAttrs,
           OPEN_ALWAYS,
           FILE_ATTRIBUTE_TEMPORARY,
           null );

      assert(hstdin!=INVALID_HANDLE_VALUE);

      STARTUPINFOA sui;
      sui.cb          = STARTUPINFOA.sizeof;
      sui.dwFlags     = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
      sui.wShowWindow = SW_HIDE;
      sui.hStdOutput  = hstdout;
      sui.hStdInput   = hstdin;

      PROCESS_INFORMATION pi;
      int res = CreateProcessA(null,
          "dir",
          null,
          null,
          true,
          CREATE_NEW_CONSOLE | REALTIME_PRIORITY_CLASS,
          null,
          null,
          &sui,
          &pi);

      DWORD size;
      size = GetFileSize(outstreamhandle, null);
      printf("%d\n", size);
      size = GetFileSize(hstdout, null);
      printf("%d\n", size);
      size = GetFileSize(instreamhandle, null);
      printf("%d\n", size);
      size = GetFileSize(hstdin, null);
      printf("%d\n", size);
      assert (size != INVALID_FILE_SIZE);

      /*
      fstdout=new BufferedFile(new File(cast(std.stream.HANDLE)
 outstreamhandle, FileMode.In | FileMode.Out ));
      foreach(char[] ln;fstdout)
          stdout.writeLine(ln);
      */

      /*
      fstdin=new BufferedFile(new File(cast(std.stream.HANDLE)
 instreamhandle, FileMode.In | FileMode.Out));
      foreach(char[] ln;fstdin)
          stdout.writeLine(ln);
      */
 }
 //-------------------------------------------

 All asserts pass. All sizes return 0. Obviosly, even in an empty
 directory, "dir" outputs something.

 Eventually, I'd like to do what's commented out.

 BTW, I've never worked with pipes before on linux, but I also found
 some code to do the same, and it's unbelievably easy. When/if I get
 this Windows thing sorted out (a couple of days, maybe?) I'll release
 the code for anyone in need of it.



Apr 19 2005
prev sibling next sibling parent reply "Ben Hinkle" <ben.hinkle gmail.com> writes:
"Regan Heath" <regan netwin.co.nz> wrote in message 
news:opsphdcp0r23k2f5 nrage.netwin.co.nz...
 As promised... Tho it should be noted that you cannot simply run "dir"
 with this. it wants an actual executable not a cmd.exe command like "dir".
 However you can run "cmd /c dir" and get a directory listing.

 It would be nice if Ben had time to look at it and make sure I have not
 abused Stream ;)

Cool stuff. My first thought is that it would be more standard to have Process be a class that has two Stream members (one for the in pipe and one for the out pipe). Something like class Process { InputStream input; OutputStream output; } ... Process proc = new Process("cmd /c dir"); stdout.writefln("%s",proc.input.readLine()); But that's just becuase it feels odd to have Process subclass Stream. But I haven't thought about it too much... See for example Java's Process and PipeStream classes.
Apr 19 2005
parent reply "Regan Heath" <regan netwin.co.nz> writes:
On Tue, 19 Apr 2005 19:30:30 -0400, Ben Hinkle <ben.hinkle gmail.com>  
wrote:
 "Regan Heath" <regan netwin.co.nz> wrote in message
 news:opsphdcp0r23k2f5 nrage.netwin.co.nz...
 As promised... Tho it should be noted that you cannot simply run "dir"
 with this. it wants an actual executable not a cmd.exe command like  
 "dir".
 However you can run "cmd /c dir" and get a directory listing.

 It would be nice if Ben had time to look at it and make sure I have not
 abused Stream ;)

Cool stuff. My first thought is that it would be more standard to have Process be a class that has two Stream members (one for the in pipe and one for the out pipe). Something like class Process { InputStream input; OutputStream output; } ... Process proc = new Process("cmd /c dir"); stdout.writefln("%s",proc.input.readLine()); But that's just becuase it feels odd to have Process subclass Stream. But I haven't thought about it too much... See for example Java's Process and PipeStream classes.

Sounds good. I wasn't really sure how it was all supposed to "hang together". So we derive a PipeStream class from Stream. In the PipeStream constructor pass IN or OUT (kinda like how File works). I might give that a go if I find some more time, or if you (or someone else) wants to, feel free. Regan
Apr 19 2005
next sibling parent reply Georg Wrede <georg.wrede nospam.org> writes:
Regan Heath wrote:
 On Tue, 19 Apr 2005 19:30:30 -0400, Ben Hinkle <ben.hinkle gmail.com>
  wrote:
 
 "Regan Heath" <regan netwin.co.nz> wrote in message 
 news:opsphdcp0r23k2f5 nrage.netwin.co.nz...
 
 As promised... Tho it should be noted that you cannot simply run
 "dir" with this. it wants an actual executable not a cmd.exe
 command like "dir". However you can run "cmd /c dir" and get a
 directory listing.
 
 It would be nice if Ben had time to look at it and make sure I
 have not abused Stream ;)

Cool stuff. My first thought is that it would be more standard to have Process be a class that has two Stream members (one for the in pipe and one for the out pipe). Something like class Process { InputStream input; OutputStream output; } ... Process proc = new Process("cmd /c dir"); stdout.writefln("%s",proc.input.readLine());


I hope you put in a stream for stderr, too!
 But that's just becuase it feels odd to have Process subclass
 Stream. But I haven't thought about it too much... See for example
 Java's Process and PipeStream classes.

Sounds good. I wasn't really sure how it was all supposed to "hang together". So we derive a PipeStream class from Stream. In the PipeStream constructor pass IN or OUT (kinda like how File works). I might give that a go if I find some more time, or if you (or someone else) wants to, feel free. Regan

Apr 19 2005
parent reply "Regan Heath" <regan netwin.co.nz> writes:
On Wed, 20 Apr 2005 09:37:29 +0300, Georg Wrede <georg.wrede nospam.org>  
wrote:
 Regan Heath wrote:
 On Tue, 19 Apr 2005 19:30:30 -0400, Ben Hinkle <ben.hinkle gmail.com>
  wrote:

 "Regan Heath" <regan netwin.co.nz> wrote in message  
 news:opsphdcp0r23k2f5 nrage.netwin.co.nz...

 As promised... Tho it should be noted that you cannot simply run
 "dir" with this. it wants an actual executable not a cmd.exe
 command like "dir". However you can run "cmd /c dir" and get a
 directory listing.
  It would be nice if Ben had time to look at it and make sure I
 have not abused Stream ;)

have Process be a class that has two Stream members (one for the in pipe and one for the out pipe). Something like class Process { InputStream input; OutputStream output; } ... Process proc = new Process("cmd /c dir"); stdout.writefln("%s",proc.input.readLine());


I hope you put in a stream for stderr, too!

:) I have actually. I started coding a new version using Ben's idea. Regan
Apr 20 2005
parent "Regan Heath" <regan netwin.co.nz> writes:
------------1VzKyz4MJjC8Pkna883lN5
Content-Type: text/plain; format=flowed; delsp=yes; charset=iso-8859-15
Content-Transfer-Encoding: 8bit

On Wed, 20 Apr 2005 22:48:38 +1200, Regan Heath <regan netwin.co.nz> wrote:
 On Wed, 20 Apr 2005 09:37:29 +0300, Georg Wrede <georg.wrede nospam.org>  
 wrote:
 Regan Heath wrote:
 On Tue, 19 Apr 2005 19:30:30 -0400, Ben Hinkle <ben.hinkle gmail.com>
  wrote:

 "Regan Heath" <regan netwin.co.nz> wrote in message  
 news:opsphdcp0r23k2f5 nrage.netwin.co.nz...

 As promised... Tho it should be noted that you cannot simply run
 "dir" with this. it wants an actual executable not a cmd.exe
 command like "dir". However you can run "cmd /c dir" and get a
 directory listing.
  It would be nice if Ben had time to look at it and make sure I
 have not abused Stream ;)

have Process be a class that has two Stream members (one for the in pipe and one for the out pipe). Something like class Process { InputStream input; OutputStream output; } ... Process proc = new Process("cmd /c dir"); stdout.writefln("%s",proc.input.readLine());


I hope you put in a stream for stderr, too!

:) I have actually. I started coding a new version using Ben's idea.

And here it is! Thoughts? Regan ------------1VzKyz4MJjC8Pkna883lN5 Content-Disposition: attachment; filename=process.d Content-Type: application/octet-stream; name=process.d Content-Transfer-Encoding: 8bit /* * Copyright (c) 2005 * Regan Heath * * Permission to use, copy, modify, distribute and sell this software * and its documentation for any purpose is hereby granted without fee, * provided that the above copyright notice appear in all copies and * that both that copyright notice and this permission notice appear * in supporting documentation. Author makes no representations about * the suitability of this software for any purpose. It is provided * "as is" without express or implied warranty. */ module lib.process; private import std.c.stdlib; import lib.pipestream; extern(C) char* strdup(char*); version(Windows) { import std.c.windows.windows; import std.windows.syserror; extern(Windows) { struct PROCESS_INFORMATION { HANDLE hProcess; HANDLE hThread; DWORD dwProcessId; DWORD dwThreadId; } alias PROCESS_INFORMATION* PPROCESS_INFORMATION, LPPROCESS_INFORMATION; struct STARTUPINFOA { DWORD cb; LPSTR lpReserved; LPSTR lpDesktop; LPSTR lpTitle; DWORD dwX; DWORD dwY; DWORD dwXSize; DWORD dwYSize; DWORD dwXCountChars; DWORD dwYCountChars; DWORD dwFillAttribute; DWORD dwFlags; WORD wShowWindow; WORD cbReserved2; LPBYTE lpReserved2; HANDLE hStdInput; HANDLE hStdOutput; HANDLE hStdError; } alias STARTUPINFOA* LPSTARTUPINFOA; struct STARTUPINFOW { DWORD cb; LPWSTR lpReserved; LPWSTR lpDesktop; LPWSTR lpTitle; DWORD dwX; DWORD dwY; DWORD dwXSize; DWORD dwYSize; DWORD dwXCountChars; DWORD dwYCountChars; DWORD dwFillAttribute; DWORD dwFlags; WORD wShowWindow; WORD cbReserved2; LPBYTE lpReserved2; HANDLE hStdInput; HANDLE hStdOutput; HANDLE hStdError; } alias STARTUPINFOW* LPSTARTUPINFOW; VOID GetStartupInfoA(LPSTARTUPINFOA lpStartupInfo); VOID GetStartupInfoW(LPSTARTUPINFOW lpStartupInfo); uint STARTF_USESHOWWINDOW = 0x00000001; uint STARTF_USESIZE = 0x00000002; uint STARTF_USEPOSITION = 0x00000004; uint STARTF_USECOUNTCHARS = 0x00000008; uint STARTF_USEFILLATTRIBUTE = 0x00000010; uint STARTF_RUNFULLSCREEN = 0x00000020; uint STARTF_FORCEONFEEDBACK = 0x00000040; uint STARTF_FORCEOFFFEEDBACK = 0x00000080; uint STARTF_USESTDHANDLES = 0x00000100; /+#if(WINVER >= 0x0400) #define STARTF_USEHOTKEY 0x00000200 #endif /* WINVER >= 0x0400 */ +/ BOOL CreateProcessA( LPCSTR lpApplicationName, LPSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCSTR lpCurrentDirectory, LPSTARTUPINFOA lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation ); BOOL CreateProcessW( LPCWSTR lpApplicationName, LPWSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCWSTR lpCurrentDirectory, LPSTARTUPINFOW lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation ); // // dwCreationFlag values // uint DEBUG_PROCESS = 0x00000001; uint DEBUG_ONLY_THIS_PROCESS = 0x00000002; uint CREATE_SUSPENDED = 0x00000004; uint DETACHED_PROCESS = 0x00000008; uint CREATE_NEW_CONSOLE = 0x00000010; uint NORMAL_PRIORITY_CLASS = 0x00000020; uint IDLE_PRIORITY_CLASS = 0x00000040; uint HIGH_PRIORITY_CLASS = 0x00000080; uint REALTIME_PRIORITY_CLASS = 0x00000100; uint CREATE_NEW_PROCESS_GROUP = 0x00000200; uint CREATE_UNICODE_ENVIRONMENT = 0x00000400; uint CREATE_SEPARATE_WOW_VDM = 0x00000800; uint CREATE_SHARED_WOW_VDM = 0x00001000; uint CREATE_FORCEDOS = 0x00002000; uint CREATE_DEFAULT_ERROR_MODE = 0x04000000; uint CREATE_NO_WINDOW = 0x08000000; uint PROFILE_USER = 0x10000000; uint PROFILE_KERNEL = 0x20000000; uint PROFILE_SERVER = 0x40000000; BOOL TerminateProcess(HANDLE hProcess, UINT uExitCode); } } version(linux) { extern (C) char* strerror(int); } class ProcessException : Exception { version(Windows) { this(char[] msg) { super(msg ~ ": " ~ sysErrorString(GetLastError())); } } version(linux) { //for some reason getErrno does not link for me? this(char[] msg) { super(msg ~ ": " ~ std.string.toString(strerror(getErrno()))); } } } class Process { this() { } this(char[] command) { this(); execute(command); } void execute(char[] command) { if (running) kill(); startProcess(command); } void kill() { if (!running) return; stopProcess(0); } void addEnv(char[] label, char[] value) { addEnv(label~"="~value); } void addEnv(char[] value) { enviroment ~= value; } private: char[][] enviroment = null; bool running = false; PipeStream pout = null; PipeStream perr = null; PipeStream pin = null; char[] readLine() { return pout.readLine(); } char[] readError() { return perr.readLine(); } void writeLine(char[] line) { pin.writeLine(line); } version(Windows) { PROCESS_INFORMATION *info = null; char* makeBlock(char[][] from) { char* result = null; uint length = 0; uint upto = 0; foreach(char[] s; from) length += s.length; //total length of strings length += from.length; //add space for a \0 for each string length++; //add space for final terminating \0 result = cast(char*)calloc(0,length); foreach(char[] s; from) { result[upto..upto+s.length] = s[0..s.length]; upto += s.length+1; } return result; } void freeBlock(char* data) { free(data); } void startProcess(char[] command) { STARTUPINFOA startup; char* env = null; try { pout = new PipeStream(); perr = new PipeStream(); pin = new PipeStream(); GetStartupInfoA(&startup); startup.hStdInput = pin.readHandle; startup.hStdOutput = pout.writeHandle; startup.hStdError = perr.writeHandle; startup.dwFlags = STARTF_USESTDHANDLES; info = new PROCESS_INFORMATION(); env = makeBlock(enviroment); if (!CreateProcessA(null,std.string.toStringz(command),null,null,true,DETACHED_PROCESS,env,null,&startup,info)) throw new ProcessException("CreateProcess"); running = true; } finally { if (env) freeBlock(env); if (running) { CloseHandle(info.hThread); pin.closeRead(); pout.closeWrite(); perr.closeWrite(); } else { if (info) info = null; pout = null; perr = null; pin = null; } } } void stopProcess(uint exitCode) { if (!TerminateProcess(info.hProcess,exitCode)) throw new ProcessException("TerminateProcess"); running = false; CloseHandle(info.hProcess); info = null; pout = null; perr = null; pin = null; } } version(linux) { int pid; char** makeBlock(char[][] from) { char** result = null; result = cast(char**)calloc(0,(enviroment.length+1) * typeid(char*).sizeof); foreach(uint i, char[] s; from) result[i] = strdup(toStringz(s)); return result; } void freeBlock(char** block) { for(uint i = 0; block[i]; i++) free(block[i]); free(block); } char[][] splitArgs(char[] string, char[] delims) { char[] delims = " \t\r\n"; char[][] results = null; bool isquot = false; int start = -1; for(int i = 0; i < string.length; i++) { if (string[i] == '\"') isquot = !isquot; if (isquot) continue; if (delims.find(string[i]) != -1) { if (start == -1) continue; results ~= string[start..i]; start = -1; continue; } if (start == -1) start = i; } return results; } void startProcess(char[] command) { try { pin = new PipeStream(); pout = new PipeStream(); perr = new PipeStream(); if (fcntl(pin.writeHandle, F_SETFD, 1) == -1) throw new ProcessException("fcntl(pin.writeHandle)"); if (fcntl(pout.readHandle, F_SETFD, 1) == -1) throw new ProcessException("fcntl(pout.readHandle)"); if (fcntl(perr.readHandle, F_SETFD, 1) == -1) throw new ProcessException("fcntl(perr.readHandle)"); if (fcntl(fileno(stdin), F_SETFD, 1) == -1) throw new ProcessException("fcntl(stdin)"); if (fcntl(fileno(stdout), F_SETFD, 1) == -1) throw new ProcessException("fcntl(stdout)"); if (fcntl(fileno(stderr), F_SETFD, 1) == -1) throw new ProcessException("fcntl(stderr)"); pid = fork(); if (pid == 0) { /* child */ //not sure if we can even throw here? if (dup2(pout.writeHandle,STDOUT_FILENO) == -1) {} //throw new ProcessException("dup2(xwrite[1])"); if (dup2(perr.writeHandle,STDERR_FILENO) == -1) {} //throw new ProcessException("dup2(xread[0])"); if (dup2(pin.readHandle,STDIN_FILENO) == -1) {} //throw new ProcessException("dup2(xread[0])"); pout.closeWrite(); perr.closeWrite(); pin.closeRead(); /* set child uid/gid here */ //if (setuid(uid) == -1) throw new ProcessException("setuid"); //if (setgid(gid) == -1) throw new ProcessException("setgid"); execve(args[0],makeBlock(splitArgs(command)),makeBlock(enviroment)); //this does not return on success //can we throw? how to notify parent of failure? exit(1); } /* parent */ running = true; } finally { if (running) { pout.closeWrite(); perr.closeWrite(); pin.closeRead(); } else { pin = null; pout = null; perr = null; } } } void stopProcess(uint dummy) { int r; if (pid == 0) return; if (kill(pid, SIGTERM) == -1) throw new ProcessException("kill"); for(uint i = 0; i < 100; i++) { r = waitpid(pid,null,WNOHANG|WUNTRACED); if (r == -1) throw new ProcessException("waitpid"); if (r == pid) break; usleep(50000); } running = false; close(output); close(input); pid = 0; } } } /+ void main() { auto Process p = new Process("cmd /c dir"); printf("%.*s",p.readLine()); } +/ ------------1VzKyz4MJjC8Pkna883lN5 Content-Disposition: attachment; filename=pipestream.d Content-Type: application/octet-stream; name=pipestream.d Content-Transfer-Encoding: 8bit /* * Copyright (c) 2005 * Regan Heath * * Permission to use, copy, modify, distribute and sell this software * and its documentation for any purpose is hereby granted without fee, * provided that the above copyright notice appear in all copies and * that both that copyright notice and this permission notice appear * in supporting documentation. Author makes no representations about * the suitability of this software for any purpose. It is provided * "as is" without express or implied warranty. */ module lib.pipestream; import std.stream; extern(C) char* strdup(char*); version(Windows) { import std.c.windows.windows; import std.windows.syserror; extern(Windows) { alias HANDLE* PHANDLE; BOOL CreatePipe( PHANDLE hReadPipe, PHANDLE hWritePipe, LPSECURITY_ATTRIBUTES lpPipeAttributes, DWORD nSize ); BOOL PeekNamedPipe( HANDLE hNamedPipe, LPVOID lpBuffer, DWORD nBufferSize, LPDWORD lpBytesRead, LPDWORD lpTotalBytesAvail, LPDWORD lpBytesLeftThisMessage ); } } version(linux) { private import std.c.stdlib; extern (C) char* strerror(int); } version(Windows) { class PipeException : Exception { this(char[] msg) { super(msg ~ ": " ~ sysErrorString(GetLastError())); } } class PipeStream : Stream { this(uint bufferSize = 0) { SECURITY_ATTRIBUTES security; security.nLength = security.sizeof; security.lpSecurityDescriptor = null; security.bInheritHandle = true; if (!CreatePipe(&read,&write,&security,bufferSize)) throw new PipeException("CreatePipe"); writeable = true; readable = true; seekable = false; isopen = true; } HANDLE readHandle() { return read; } HANDLE writeHandle() { return write; } void closeRead() { CloseHandle(readHandle); read = INVALID_HANDLE_VALUE; readable = false; if (!writeable) isopen = false; } void closeWrite() { CloseHandle(writeHandle); write = INVALID_HANDLE_VALUE; writeable = false; if (!readable) isopen = false; } override void close() { closeRead(); closeWrite(); } override ulong seek(long offset, SeekPos whence) { assertSeekable(); } override size_t readBlock(void* buffer, size_t size) { size_t bytes = 0; assertReadable(); if (!ReadFile(readHandle,buffer,size,&bytes,null)) throw new PipeException("ReadFile"); return bytes; } override size_t available() { size_t bytes = 0; assertReadable(); if (!PeekNamedPipe(readHandle,null,0,null,&bytes,null)) throw new PipeException("PeekNamedPipe"); return bytes; } override size_t writeBlock(void* buffer, size_t size) { size_t bytes = 0; assertWriteable(); if (!WriteFile(writeHandle,buffer,size,&bytes,null)) throw new PipeException("WriteFile"); return bytes; } override void flush() { assertWriteable(); FlushFileBuffers(writeHandle); } private: HANDLE write = INVALID_HANDLE_VALUE; HANDLE read = INVALID_HANDLE_VALUE; } } version(linux) { class PipeException : Exception { //for some reason getErrno does not link for me? this(char[] msg) { super(msg ~ ": " ~ std.string.toString(strerror(getErrno()))); } } class PipeStream : Stream { this(uint dummy = 0) { if (pipe(handle) == -1) throw new PipeException("pipe(handle)"); writeable = true; readable = true; seekable = false; isopen = true; } int readHandle() { return handle[0]; } int writeHandle() { return handle[1]; } void closeRead() { close(readHandle); readable = false; if (!writeable) isopen = false; } void closeWrite() { close(writeHandle); writeable = false; if (!readable) isopen = false; } override void close() { closeRead(); closeWrite(); } override ulong seek(long offset, SeekPos whence) { assertSeekable(); } override size_t readBlock(void* buffer, size_t size) { size_t bytes; assertReadable(); bytes = read(readHandle,buffer,size); if (bytes == -1) throw new PipeException("read(handle[0])"); return bytes; } override size_t available() { size_t bytes; assertReadable(); if (ioctl(readHandle,FIONREAD,&bytes) == -1) throw new PipeException("ioctl(handle[0])"); return bytes; } override size_t writeBlock(void* buffer, size_t size) { size_t bytes; assertWriteable(); bytes = write(writeHandle,buffer,size); if (bytes == -1) throw new PipeException("write(handle[1])"); return bytes; } override void flush() { assertWriteable(); //writeHandle } private: int handle[2]; } } /+ class Process : Stream { alias std.stdio.writefln debugf; this() { super(); seekable = false; readable = true; writeable = true; isopen = false; } this(char[] command) { this(); open(command); } ~this() { close(); } void addEnv(char[] label, char[] value) { addEnv(label~"="~value); } void addEnv(char[] value) { enviroment ~= value; } void open(char[] command) { if (isopen) close(); startProcess(command); } override void close() { if (!isopen) return ; flush(); stopProcess(0); isopen = false; } override ulong seek(long offset, SeekPos whence) { assertSeekable(); } version(Windows) { override size_t readBlock(void* buffer, size_t size) { size_t bytes = 0; if (!isopen) return 0; if (!ReadFile(output,buffer,size,&bytes,null)) throw new ProcessException("ReadFile"); return bytes; } override size_t writeBlock(void* buffer, size_t size) { size_t bytes = 0; if (!isopen) return 0; if (!WriteFile(input,buffer,size,&bytes,null)) throw new ProcessException("WriteFile"); return bytes; } override size_t available() { size_t bytes = 0; if (!isopen) return 0; if (!PeekNamedPipe(output,null,0,null,&bytes,null)) throw new ProcessException("PeekNamedPipe"); return bytes; } override void flush() { if (!isopen) return ; FlushFileBuffers(output); } } version(linux) { override size_t readBlock(void* buffer, size_t size) { size_t bytes; bytes = read(output,buffer,size); if (bytes == -1) throw new ProcessException("read"); return bytes; } override size_t writeBlock(void* buffer, size_t size) { size_t bytes; bytes = write(output,buffer,size); if (bytes == -1) throw new ProcessException("write"); return bytes; } override size_t available() { size_t bytes; if (ioctl(output,FIONREAD,&bytes) == -1) throw new ProcessException("ioctl"); return bytes; } } private: char[][] enviroment = null; version(Windows) { HANDLE output = INVALID_HANDLE_VALUE; HANDLE input = INVALID_HANDLE_VALUE; PROCESS_INFORMATION* info = null; int bufferSize = 0; char* makeBlock(char[][] from) { char* result = null; uint length = 0; uint upto = 0; foreach(char[] s; from) length += s.length; //total length of strings length += from.length; //add space for a \0 for each string length++; //add space for final terminating \0 result = cast(char*)calloc(0,length); foreach(char[] s; from) { result[upto..upto+s.length] = s[0..s.length]; upto += s.length+1; } return result; } void freeBlock(char* data) { free(data); } void startProcess(char[] command) { HANDLE read1,read2,read3,write1,write2,write3; SECURITY_ATTRIBUTES security; STARTUPINFOA startup; char* env; read1 = read2 = read3 = write1 = write2 = write2 = INVALID_HANDLE_VALUE; security.nLength = security.sizeof; security.lpSecurityDescriptor = null; security.bInheritHandle = true; env = null; try { if (!CreatePipe(&read1,&write1,&security,bufferSize)) throw new ProcessException("CreatePipe"); if (!CreatePipe(&read2,&write2,&security,bufferSize)) throw new ProcessException("CreatePipe"); if (!CreatePipe(&read3,&write3,&security,bufferSize)) throw new ProcessException("CreatePipe"); GetStartupInfoA(&startup); startup.hStdInput = read1; startup.hStdOutput = write2; startup.hStdError = write3; startup.dwFlags = STARTF_USESTDHANDLES; info = new PROCESS_INFORMATION(); env = makeBlock(enviroment); if (!CreateProcessA(null,toStringz(command),null,null,true,DETACHED_PROCESS,env,null,&startup,info)) throw new ProcessException("CreateProcess"); input = write1; output = read2; isopen = true; } finally { if (!isopen && read2 != INVALID_HANDLE_VALUE) CloseHandle(read2); if (!isopen && write1 != INVALID_HANDLE_VALUE) CloseHandle(write1); if (read1 != INVALID_HANDLE_VALUE) CloseHandle(read1); if (read3 != INVALID_HANDLE_VALUE) CloseHandle(read3); if (write2 != INVALID_HANDLE_VALUE) CloseHandle(write2); if (write3 != INVALID_HANDLE_VALUE) CloseHandle(write3); if (info) CloseHandle(info.hThread); if (env) freeBlock(env); } } void stopProcess(uint exitCode) { if (!TerminateProcess(info.hProcess,exitCode)) throw new ProcessException("TerminateProcess"); isopen = false; CloseHandle(info.hProcess); delete info; info = null; CloseHandle(output); output = INVALID_HANDLE_VALUE; CloseHandle(input); input = INVALID_HANDLE_VALUE; } } version(linux) { int output = 0; int input = 0; int pid = 0; char** makeBlock(char[][] from) { char** result = null; result = cast(char**)calloc(0,(enviroment.length+1) * typeid(char*).sizeof); foreach(uint i, char[] s; from) result[i] = strdup(toStringz(s)); return result; } void freeBlock(char** block) { for(uint i = 0; block[i]; i++) free(block[i]); free(block); } char[][] splitArgs(char[] string, char[] delims) { char[] delims = " \t\r\n"; char[][] results = null; bool isquot = false; int start = -1; for(int i = 0; i < string.length; i++) { if (string[i] == '\"') isquot = !isquot; if (isquot) continue; if (delims.find(string[i]) != -1) { if (start == -1) continue; results ~= string[start..i]; start = -1; continue; } if (start == -1) start = i; } return results; } void startProcess(char[] command) { int xwrite[2]; int xread[2]; try { if (pipe(xwrite) == -1) throw new ProcessException("pipe(xwrite)"); if (pipe(xread) == -1) throw new ProcessException("pipe(xread)"); if (fcntl(input, F_SETFD, 1) == -1) throw new ProcessException("fcntl(input)"); if (fcntl(output, F_SETFD, 1) == -1) throw new ProcessException("fcntl(output)"); if (fcntl(fileno(stdin), F_SETFD, 1) == -1) throw new ProcessException("fcntl(stdin)"); if (fcntl(fileno(stdout), F_SETFD, 1) == -1) throw new ProcessException("fcntl(stdout)"); if (fcntl(fileno(stderr), F_SETFD, 1) == -1) throw new ProcessException("fcntl(stderr)"); pid = fork(); if (pid == 0) { /* child */ char** args = null; char** env = null; //not sure if we can even throw here? if (dup2(xwrite[1],STDOUT_FILENO) == -1) {} //throw new ProcessException("dup2(xwrite[1])"); if (dup2(xread[0], STDIN_FILENO) == -1) {} //throw new ProcessException("dup2(xread[0])"); close(xwrite[1]); close(xread[0]); /* set child uid/gid here */ //if (setuid(uid) == -1) throw new ProcessException("setuid"); //if (setgid(gid) == -1) throw new ProcessException("setgid"); args = makeBlock(splitArgs(command)); env = makeBlock(enviroment); execve(args[0],args,env); //this does not return on success //can we throw? how to notify parent of failure? exit(1); } /* parent */ isopen = true; } finally { close(xwrite[1]); close(xread[0]); } } void stopProcess(uint dummy) { int r; if (pid == 0) return; if (kill(pid, SIGTERM) == -1) throw new ProcessException("kill"); for(uint i = 0; i < 100; i++) { r = waitpid(pid,null,WNOHANG|WUNTRACED); if (r == -1) throw new ProcessException("waitpid"); if (r == pid) break; usleep(50000); } isopen = false; close(output); close(input); pid = 0; } } } +/ ------------1VzKyz4MJjC8Pkna883lN5--
Apr 20 2005
prev sibling parent reply "Ben Hinkle" <ben.hinkle gmail.com> writes:
 So we derive a PipeStream class from Stream. In the PipeStream constructor 
 pass IN or OUT (kinda like how File works). I might give that a go if I 
 find some more time, or if you (or someone else) wants to, feel free.

Sounds reasonable. It might be worth subclassing (or maybe even just reusing) std.stream.File since a pipe is (I think) implemented as file descriptors on the OS's I know about. For example on Windows pipes use ReadFile and WriteFile. I don't know if you were planning on suggesting it to Walter but it might be nice to put the Process class into std.process, too.
Apr 20 2005
parent "Regan Heath" <regan netwin.co.nz> writes:
On Wed, 20 Apr 2005 08:24:42 -0400, Ben Hinkle <ben.hinkle gmail.com>  
wrote:
 So we derive a PipeStream class from Stream. In the PipeStream  
 constructor
 pass IN or OUT (kinda like how File works). I might give that a go if I
 find some more time, or if you (or someone else) wants to, feel free.

Sounds reasonable. It might be worth subclassing (or maybe even just reusing) std.stream.File since a pipe is (I think) implemented as file descriptors on the OS's I know about. For example on Windows pipes use ReadFile and WriteFile.

They do.. if anything I would think that std.stream.File would use PipeStream (see my latest code). The reason I say this is that std.stream.File uses pipes, but also calls CreateFile to create a file on a disk. So it seems to me FileStream extends PipeStream, not the other way round. We would need to move some of the functionality in std.stream.File into PipeStream though i.e. seekability check, seek routine, etc.
 I don't know if you were planning on suggesting it to Walter but it  
 might be nice to put the Process class into std.process, too.

If he thinks it's useful, he's welcome to it. Regan
Apr 20 2005
prev sibling parent reply "Carlos Santander B." <csantander619 gmail.com> writes:
Regan Heath wrote:
 As promised... Tho it should be noted that you cannot simply run "dir"  
 with this. it wants an actual executable not a cmd.exe command like 
 "dir".  However you can run "cmd /c dir" and get a directory listing.
 
 It would be nice if Ben had time to look at it and make sure I have not  
 abused Stream ;)
 
 Regan
 

Great. Thanks. Maybe this should go to Phobos? What do you guys think? -- Carlos Santander Bernal
Apr 19 2005
parent reply "Regan Heath" <regan netwin.co.nz> writes:
On Tue, 19 Apr 2005 20:31:17 -0500, Carlos Santander B.  
<csantander619 gmail.com> wrote:
 Regan Heath wrote:
 As promised... Tho it should be noted that you cannot simply run "dir"   
 with this. it wants an actual executable not a cmd.exe command like  
 "dir".  However you can run "cmd /c dir" and get a directory listing.
  It would be nice if Ben had time to look at it and make sure I have  
 not  abused Stream ;)
  Regan

Great. Thanks. Maybe this should go to Phobos? What do you guys think?

It's fine by me :) However, Ben has pointed out a few design changes which I think are a good idea. Regan
Apr 19 2005
parent jicman <jicman_member pathlink.com> writes:
Well, what are you waiting for?  Get at it! :-)

jic

Regan Heath says...
On Tue, 19 Apr 2005 20:31:17 -0500, Carlos Santander B.  
<csantander619 gmail.com> wrote:
 Regan Heath wrote:
 As promised... Tho it should be noted that you cannot simply run "dir"   
 with this. it wants an actual executable not a cmd.exe command like  
 "dir".  However you can run "cmd /c dir" and get a directory listing.
  It would be nice if Ben had time to look at it and make sure I have  
 not  abused Stream ;)
  Regan

Great. Thanks. Maybe this should go to Phobos? What do you guys think?

It's fine by me :) However, Ben has pointed out a few design changes which I think are a good idea. Regan

Apr 20 2005