March 15, 2005 Re: I have a dream that... | ||||
---|---|---|---|---|
| ||||
Posted in reply to J C Calvarese | > 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, ©, 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 Re: I have a dream that... | ||||
---|---|---|---|---|
| ||||
Posted in reply to Niall FitzGibbon | 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) |
Copyright © 1999-2021 by the D Language Foundation