March 15, 2005
> WaitForSingleObject(process, INFINITE);

I've been bitten by this one :).

Charlie


"J C Calvarese" <jcc7@cox.net> wrote in message news:d12i3m$4gk$1@digitaldaemon.com...
> jicman wrote:
> > Unknown,
> >
> > this would work.  I hadn't noticed this.  Still, though, a systemOut()
also be
> > nice.
> >
> > thanks.
> >
> > Is there any way that you can zip this and attach it again?  I don't
want to
> > copy and paste it and I am using the web browser and not the news
reader.
>
> Here it is (watch out for word-wrap):
>
> module popen;
>
> private import std.c.stdio;
> private import std.stream;
>
> unittest
> {
> // Just some old command; testing the stderr piping.
> ProcessStream test = new ProcessStream("diff 2>&1");
>
> std.stream.stdout.writeLine(test.readLine());
> std.stream.stdout.writeLine(test.readLine());
> std.stream.stdout.flush();
>
> // Talk about a useless waste :P!
> test = new ProcessStream("cat", FileMode.Out);
>
> test.writeLine("There's no place like stdout.");
> }
>
> // ProcessStream class - used to read or write (can't do both) from/to a
> process.
> class ProcessStream: Stream
> {
> // The FILE* handle to the process's output.
> private FILE* handle = null;
> // Seems this is private, so I had to override it; whether the file is
> open.
> private override bool isopen = false;
>
> // Create a new ProcessStream without any actual command/arguments.
> this()
> {
> super();
>
> // Clean up, this sets all the same variables anyway.
> this.close();
> }
>
> // A quick way to create the class and open it at the same time...
> this(char[] command, FileMode mode = FileMode.In)
> {
> super();
>
> this.open(command, mode);
> }
>
> ~this()
> {
> this.close();
> }
>
> void open(char[] command, FileMode mode = FileMode.In)
> {
> // Make sure we're all closed off  ;) .
> this.close();
>
> // These are pretty simple and come nearly straight from the File class.
> this.readable = cast(bit) (mode & FileMode.In);
> this.writeable = cast(bit) (mode & FileMode.Out);
> this.seekable = false;
>
> // TODO: Binary flag?
> this.handle = popen(command, mode == FileMode.In ? "r" : "w");
>
> // Throw an OpenException if anything goes arye.
> if (this.handle == null)
> {
> this.isopen = false;
> throw new OpenException("couldn't pipe " ~ command);
> }
> }
>
> // Close the stream, or make sure everything is clean if the stream
> isn't open.
> override void close()
> {
> if (this.isopen && this.handle != null)
> pclose(this.handle);
>
> this.handle = null;
> this.isopen = false;
> this.readable = false;
> this.writeable = false;
> this.seekable = false;
> }
>
> override uint readBlock(void* buffer, uint size)
> in
> {
> // Not only must it be readable, but the handle must not be null
> (isopen might be equivalent.)
> assert(this.readable);
> assert(this.handle != null);
> }
> body
> {
> // Map straight to fread.
> return fread(buffer, 1, size, this.handle);
> }
>
> override uint writeBlock(void* buffer, uint size)
> in
> {
> // Check to make sure we're not writing to an unwritable stream.
> assert(this.writeable);
> assert(this.handle != null);
> }
> body
> {
> // Again, fwrite works perfectly for FILEs.
> return fwrite(buffer, 1, size, this.handle);
> }
>
> override ulong seek(long offset, SeekPos whence)
> {
> // If I throw an error here, nothing works, so zero will have to do.
> return 0;
> }
> }
>
> version (linux)
> {
> // Blissfully easy.
> extern (C)
> {
> FILE* popen(char* command, char* type);
> int pclose(FILE* stream);
> }
> }
> else version (Windows)
> {
> private import std.c.windows.windows;
>
> // For memset.
> private import std.string;
>
> // Some of this come from winbase.h.
> extern (Windows)
> {
> struct PROCESS_INFORMATION
> {
> HANDLE hProcess;
> HANDLE hThread;
> DWORD dwProcessId;
> DWORD dwThreadId;
> }
> struct STARTUPINFO
> {
> DWORD cb;
> LPTSTR lpReserved;
> LPTSTR lpDesktop;
> LPTSTR 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 HANDLE* PHANDLE, LPHANDLE;
> alias STARTUPINFO* LPSTARTUPINFO;
> alias PROCESS_INFORMATION* LPPROCESS_INFORMATION;
> alias int intptr_t;
>
> const int O_BINARY = 0x8000, O_TEXT = 0x4000;
> const int O_RDONLY = 0, O_WRONLY = 1;
> const uint STD_INPUT_HANDLE = cast(uint) -10;
> const uint STD_OUTPUT_HANDLE = cast(uint) -11;
> const int STARTF_USESTDHANDLES = 0x00000100;
> const int DUPLICATE_CLOSE_SOURCE = 0x00000001;
> const int DUPLICATE_SAME_ACCESS = 0x00000002;
> const int NORMAL_PRIORITY_CLASS = 0x00000020;
> const int INFINITE = 0x0FFFFFFF;
>
> BOOL CloseHandle(HANDLE hObject);
> BOOL CreatePipe(PHANDLE hReadPipe, PHANDLE hWritePipe,
> LPSECURITY_ATTRIBUTES lpPipeAttributes, DWORD nSize);
> BOOL CreateProcessA(LPCTSTR lpApplicationName, LPTSTR lpCommandLine,
> LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES
> lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID
> lpEnvironment, LPCTSTR lpCurrentDirectory, LPSTARTUPINFO lpStartupInfo,
> LPPROCESS_INFORMATION lpProcessInformation);
> BOOL CreateProcessW(LPCTSTR lpApplicationName, LPTSTR lpCommandLine,
> LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES
> lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID
> lpEnvironment, LPCTSTR lpCurrentDirectory, LPSTARTUPINFO lpStartupInfo,
> LPPROCESS_INFORMATION lpProcessInformation);
> BOOL DuplicateHandle(HANDLE hSourceProcessHandle, HANDLE
> hSourceHandle, HANDLE hTargetProcessHandle, LPHANDLE lpTargetHandle,
> DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwOptions);
> BOOL GetExitCodeProcess(HANDLE hProcess, LPDWORD lpExitCode);
> HANDLE GetStdHandle(DWORD nStdHandle);
> DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds);
> }
>
> // Grab some useful C functions too.
> extern (C)
> {
> int _open_osfhandle(intptr_t osfhandle, int flags);
> FILE* fdopen(int fd, char *mode);
> }
>
> /* Okay, this is essentially a map from FILE pointer to HANDLE.
>    It's needed so the HANDLE can be freed at the same time the FILE
>    is, without changing the syntax of popen().  Alternatively, it
>    could be encapsulated within a class; but I like popen ^_^. */
> private HANDLE[FILE*] popenBuffer;
>
> /* This is the actual implementation of popen.  I put this together
>    from various examples on Google. */
> FILE* popen(char* command, char* type)
> {
> STARTUPINFO startup;
> PROCESS_INFORMATION process;
> SECURITY_ATTRIBUTES security;
> HANDLE inH, outH;
>
> // The handle needs to be inherited...
> security.nLength = SECURITY_ATTRIBUTES.sizeof;
> security.bInheritHandle = true;
> security.lpSecurityDescriptor = null;
>
> // Attempt to create the pipe between our two handles.
> if (!CreatePipe(&inH, &outH, &security, 2048L))
> return null;
>
> memset(&startup, 0, STARTUPINFO.sizeof);
> memset(&process, 0, PROCESS_INFORMATION.sizeof);
>
> startup.cb = STARTUPINFO.sizeof;
> startup.dwFlags = STARTF_USESTDHANDLES;
> // TODO: Check consistency on Linux (no stderr.)
> startup.hStdError = find(toString(command), "2>&1") != -1 ? outH :
> INVALID_HANDLE_VALUE;
>
> // A quick nested function to simplify the calls...
> void duplicateHandle(HANDLE fix)
> {
> HANDLE copy, self = GetCurrentProcess();
>
> if (!DuplicateHandle(self, fix, self, &copy, 0, FALSE,
> DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE))
> fix = null;
> else
> fix = copy;
> }
>
> // We're going to use our handles - so, if we're reading we want to
> capture its stdout.
> if (type[0] == 'r')
> {
> duplicateHandle(inH);
> startup.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
> startup.hStdOutput = outH;
> }
> // Otherwise, we're writing, so we want to capture stdin.
> else
> {
> duplicateHandle(outH);
> startup.hStdInput = inH;
> startup.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
> }
>
> // TODO: Why won't CreateProcessW work here at all?
> if (!CreateProcessA(null, command, &security, &security,
> security.bInheritHandle, NORMAL_PRIORITY_CLASS, null, null, &startup,
> &process))
> return null;
> CloseHandle(process.hThread);
>
> // Now, we want to actually get the FILE (to emulate popen) so...
> int fno, mode = strlen(type) == 2 && type[1] == 'b' ? O_BINARY : O_TEXT;
> if (type[0] == 'r')
> {
> fno = _open_osfhandle(cast(long) inH, O_RDONLY | mode);
> CloseHandle(outH);
> }
> else
> {
> fno = _open_osfhandle(cast(long) outH, O_WRONLY | mode);
> CloseHandle(inH);
> }
>
> // Now it's child's play.  Open 'er up and stick the process handle in
> the lookup.
> // TODO: Why won't using plain type work?
> FILE* stream = fdopen(fno, "r+");
> popenBuffer[stream] = process.hProcess;
>
> return stream;
> }
>
> int pclose(FILE* stream)
> {
> DWORD termstat = 0;
> HANDLE process = popenBuffer[stream];
>
> // Flush any buffer(s) and say sianara.
> fflush(stream);
> fclose(stream);
>
> // Wait for it to quit and get its exit code... I hope INFINITE is
> okay here.
> WaitForSingleObject(process, INFINITE);
> GetExitCodeProcess(process, &termstat);
> CloseHandle(process);
>
> // Get rid of useless clutter explicitly.
> delete popenBuffer[stream];
>
> return termstat;
> }
> }
> else
> {
> // This probably isn't needed; I'm sure most UNIX based operating
> systems have popen, no?  Does Mac OS X?
> static assert(0);
> }
>
> --
> Justin (a/k/a jcc7)
> http://jcc_7.tripod.com/d/


March 15, 2005
This would be very cool , as in C++ this annoyingly hard to do with win32 :S.

Charlie


"Niall FitzGibbon" <billdoor@gmail.com> wrote in message news:d12chg$312o$1@digitaldaemon.com...
> jicman wrote:
>
> > Then, sorry to say it, but why even ask those questions?  A nice yes or
no would
> > suffice. ;-)  Just kidding man, but this is one of the reasons why this
is
> > getting a little too much bandwidth.  If you didn't care, then why
provoke with
> > the questions?  Sorry, but... ;-)
>
> Just interested, that's all.  Sorry if my post seemed confrontational or
>   unnecessary :)
>
> I think such a feature would be good, but as I said I think it would be nicer to be able to retrieve the exit value *and* the stream output(s) with a single call -- in some situations there would be a need for both.
>
> How about just an overload of the existing function?
>
> int system(char[] command, out char[] stdOut, out char[] stdErr)


1 2
Next ›   Last »