/* * Sorta a popen for Windows, except intead of FILE * * returns a PIPE * that has HANDLEs for childs stdin/stdout/stderr */ #include "fakepopen.h" #include #include "ut_string_class.h" #include "ut_debugmsg.h" // queries OS version, returns true if NT or false otherwise (9x) static inline bool isNT(void) { OSVERSIONINFO osvi; osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); if (GetVersionEx(&osvi)) return (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT)?true:false; else return false; // maybe true? we don't really know do we } // spawn specified command and inherit handles [stdout] static bool CreateChildProcess(char * appName, char *cmdline, PROCESS_INFORMATION *procInfo, HANDLE hChildStdIn, HANDLE hChildStdOut, HANDLE hChildStdErr, WORD showWnd=0) { // PROCESS_INFORMATION procInfo; STARTUPINFO startInfo; //initialize structures used to return info ZeroMemory( procInfo, sizeof(PROCESS_INFORMATION) ); ZeroMemory( &startInfo, sizeof(STARTUPINFO) ); startInfo.cb = sizeof(STARTUPINFO); startInfo.dwFlags = STARTF_USESTDHANDLES; if (showWnd) startInfo.dwFlags |= STARTF_USESHOWWINDOW; startInfo.wShowWindow = showWnd; /* eg SW_HIDE */ startInfo.hStdOutput = hChildStdOut; startInfo.hStdInput = hChildStdIn; startInfo.hStdError = hChildStdErr; // Create the child process. return (CreateProcess( appName, // application module to execute cmdline, // command line NULL, // process security attributes NULL, // primary thread security attributes TRUE, // handles are inherited *** 0, // creation flags NULL, // use parent's environment NULL, // use parent's current directory &startInfo, // STARTUPINFO pointer procInfo // receives PROCESS_INFORMATION ) != 0); } static bool getPipe(HANDLE &hChild, HANDLE &hParent, bool modeR=false) { HANDLE hChildNonInherit; SECURITY_ATTRIBUTES saAttr; // Set the bInheritHandle flag so pipe handles are inherited. saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); saAttr.bInheritHandle = TRUE; saAttr.lpSecurityDescriptor = NULL; // Create a pipe for the child process. { BOOL ok; if (modeR) ok = CreatePipe(&hChild, &hChildNonInherit, &saAttr, 0); else ok = CreatePipe(&hChildNonInherit, &hChild, &saAttr, 0); if (!ok) { UT_DEBUGMSG(("KJD: pipe creation failed\n")); return false; } } // Create noninheritable handle and close the inheritable handle. if (DuplicateHandle(GetCurrentProcess(), hChildNonInherit, GetCurrentProcess(), &hParent , 0, FALSE, DUPLICATE_SAME_ACCESS) == 0) { UT_DEBUGMSG(("KJD: DuplicateHandle failed\n")); CloseHandle(hChildNonInherit); CloseHandle(hChild); return INVALID_HANDLE_VALUE; } CloseHandle(hChildNonInherit); return true; } // Win32 popen-ish read mode for spawned process, ie returns HANDLE to child's stdout/in/err // Only those Windows programs that actually write to standard output will work as expected. PIPE * popen(char *command, const char *mode) { HANDLE hChildStdoutWr, hChildStdoutRdDup, hChildStderrWr, hChildStderrRdDup, hChildStdinRd, hChildStdinWrDup; bool fSuccess; PIPE *p = new PIPE; if (p == NULL) return NULL; ZeroMemory(p, sizeof(PIPE)); // determine mode if (strchr(mode, '+')) p->mode = FAKEPOPEN_MODE_READ | FAKEPOPEN_MODE_WRITE; else if (strchr(mode, 'w')) p->mode = FAKEPOPEN_MODE_WRITE; else p->mode = FAKEPOPEN_MODE_READ; // create pipes if (!getPipe(hChildStdoutWr, hChildStdoutRdDup)) { delete p; return NULL; } if (!getPipe(hChildStderrWr, hChildStderrRdDup)) { CloseHandle(hChildStdoutWr); CloseHandle(hChildStdoutRdDup); delete p; return NULL; } if (!getPipe(hChildStdinRd, hChildStdinWrDup, true)) { CloseHandle(hChildStderrWr); CloseHandle(hChildStderrRdDup); CloseHandle(hChildStdoutWr); CloseHandle(hChildStdoutRdDup); delete p; return NULL; } // popen runs /bin/sh 'command', perhaps we should do something similar // or at least change it to call associated program, eg active perl for *.pl files // otherwise only *.exe, *.com, & *.bat will work // We run cmd.exe for NT and command.com for 9x UT_String cmd; if (isNT()) { cmd += "cmd.exe /C "; // NOTE: command should already be quoted if needed } else { // This is purposely not 'cmd += "command.com /C ";' as it results in hang on W95 // TODO: figure out why invoking command.com causes Abi to stop responding on W95. } cmd += command; // Now create the child process. if (! (fSuccess = CreateChildProcess(NULL, (char *)cmd.c_str(), &(p->procInfo), hChildStdinRd, hChildStdoutWr, hChildStderrWr) )) { UT_DEBUGMSG(("KJD: Create process failed\n")); } // VERY IMPORTANT, do not forget to close these handles, otherwise its never done reading. CloseHandle(hChildStdoutWr); CloseHandle(hChildStderrWr); CloseHandle(hChildStdinRd); if (!fSuccess) { CloseHandle(hChildStdinWrDup); CloseHandle(hChildStderrRdDup); CloseHandle(hChildStdoutRdDup); delete p; return NULL; } else { // if we gwant to be able to write to child's stdin if (p->mode & FAKEPOPEN_MODE_WRITE) { p->hChildStdIn = hChildStdinWrDup; } else { CloseHandle(hChildStdinWrDup); p->hChildStdIn = INVALID_HANDLE_VALUE; } // if we want to be able to read from child's stdout or stderr if (p->mode & FAKEPOPEN_MODE_READ) { p->hChildStdOut = hChildStdoutRdDup; p->hChildStdErr = hChildStderrRdDup; } else { CloseHandle(hChildStdoutRdDup); CloseHandle(hChildStderrRdDup); p->hChildStdOut = INVALID_HANDLE_VALUE; p->hChildStdErr = INVALID_HANDLE_VALUE; } p->size = sizeof(PIPE); return p; } } // The pclose() function waits for the associated process to terminate and // returns the exit status of the command as returned by GetExitCodeProcess(). int pclose(PIPE *p) { // validate if ((p==NULL) || !p->size) return -1; // close our end of the pipe if (p->hChildStdIn != INVALID_HANDLE_VALUE) CloseHandle(p->hChildStdIn); if (p->hChildStdOut != INVALID_HANDLE_VALUE) CloseHandle(p->hChildStdOut); if (p->hChildStdErr != INVALID_HANDLE_VALUE) CloseHandle(p->hChildStdErr); // wait for process to terminate DWORD status = -1; if (p->procInfo.hProcess) { while ((GetExitCodeProcess(p->procInfo.hProcess, &status))?(status == STILL_ACTIVE):0) { Sleep(10); // milliseconds } } ZeroMemory(p, sizeof(PIPE)); return status; }