digitalmars.D.learn - capturing process output on windows
- "Carlos Santander B." <csantander619 gmail.com> Apr 17 2005
- "Andrew Fedoniouk" <news terrainformatica.com> Apr 17 2005
- "Carlos Santander B." <csantander619 gmail.com> Apr 18 2005
- "Unknown W. Brackets" <unknown simplemachines.org> Apr 18 2005
- "Carlos Santander B." <csantander619 gmail.com> Apr 18 2005
- "Carlos Santander B." <csantander619 gmail.com> Apr 18 2005
- David Medlock <nospam nospam.com> Apr 18 2005
- "Regan Heath" <regan netwin.co.nz> Apr 18 2005
- "Regan Heath" <regan netwin.co.nz> Apr 19 2005
- "Regan Heath" <regan netwin.co.nz> Apr 19 2005
- "Ben Hinkle" <ben.hinkle gmail.com> Apr 19 2005
- "Regan Heath" <regan netwin.co.nz> Apr 19 2005
- Georg Wrede <georg.wrede nospam.org> Apr 19 2005
- "Regan Heath" <regan netwin.co.nz> Apr 20 2005
- "Regan Heath" <regan netwin.co.nz> Apr 20 2005
- "Ben Hinkle" <ben.hinkle gmail.com> Apr 20 2005
- "Regan Heath" <regan netwin.co.nz> Apr 20 2005
- "Carlos Santander B." <csantander619 gmail.com> Apr 19 2005
- "Regan Heath" <regan netwin.co.nz> Apr 19 2005
- jicman <jicman_member pathlink.com> Apr 20 2005
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
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
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
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
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
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
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
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
------------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
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
"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
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
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
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
------------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
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
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
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
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
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









"Carlos Santander B." <csantander619 gmail.com> 