From e7c70ac1be1f21baace1f40be795095193f2a6a0 Mon Sep 17 00:00:00 2001 From: "U-olece-PC\\olece" Date: Fri, 4 Dec 2015 11:43:38 +0100 Subject: [PATCH] Added Windows process support. Also temporarily using dialogs_unix on Windows because of missing function in MSYS2. --- src/CMakeLists.txt | 2 +- src/process.h | 12 +-- src/process_unix.cc | 11 ++- src/process_win.cc | 208 +++++++++++++++++++++++++++++++++++++++++++- src/terminal.cc | 2 +- src/terminal.h | 2 +- 6 files changed, 220 insertions(+), 17 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5d9c3b0..b81bca4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -94,7 +94,7 @@ set(source_files juci.h if(MSYS) list(APPEND source_files process_win.cc) - list(APPEND source_files dialogs_win.cc) + list(APPEND source_files dialogs_unix.cc) #dialogs_win.cc does not work any more because of missing SHCreateItemFromParsingName else() list(APPEND source_files process_unix.cc) list(APPEND source_files dialogs_unix.cc) diff --git a/src/process.h b/src/process.h index 3c8bfe2..ca05c64 100644 --- a/src/process.h +++ b/src/process.h @@ -1,19 +1,19 @@ #ifndef JUCI_PROCESS_H_ #define JUCI_PROCESS_H_ -#include #include #include #include #include #include - #ifdef _WIN32 - -#else +#include + typedef HANDLE process_id_type; + typedef HANDLE file_descriptor_type; +#else +#include typedef pid_t process_id_type; typedef int file_descriptor_type; - typedef int exit_code_type; #endif class Process { @@ -28,7 +28,7 @@ public: ///Get the process id of the started process. process_id_type get_id() {return id;} ///Wait until process is finished, and return exit_code. - exit_code_type get_exit_code(); + int get_exit_code(); bool write(const char *bytes, size_t n); ///Kill a given process id. diff --git a/src/process_unix.cc b/src/process_unix.cc index 84924ea..74bbe30 100644 --- a/src/process_unix.cc +++ b/src/process_unix.cc @@ -1,19 +1,18 @@ #include "process.h" #include -#include #include #include #include //TODO: remove using namespace std; //TODO: remove -pid_t Process::open(const std::string &command, const std::string &path) { +process_id_type Process::open(const std::string &command, const std::string &path) { if(use_stdin) - stdin_fd=std::unique_ptr(new int); + stdin_fd=std::unique_ptr(new file_descriptor_type); if(read_stdout) - stdout_fd=std::unique_ptr(new int); + stdout_fd=std::unique_ptr(new file_descriptor_type); if(read_stderr) - stderr_fd=std::unique_ptr(new int); + stderr_fd=std::unique_ptr(new file_descriptor_type); int stdin_p[2], stdout_p[2], stderr_p[2]; @@ -39,7 +38,7 @@ pid_t Process::open(const std::string &command, const std::string &path) { return -1; } - pid_t pid = fork(); + process_id_type pid = fork(); if (pid < 0) { if(stdin_fd) close(stdin_p[0]); diff --git a/src/process_win.cc b/src/process_win.cc index b61c0db..5fa2e38 100644 --- a/src/process_win.cc +++ b/src/process_win.cc @@ -1,3 +1,207 @@ #include "process.h" -#include -#include +#include + +#include //TODO: remove +using namespace std; //TODO: remove + +//Based on the example at https://msdn.microsoft.com/en-us/library/windows/desktop/ms682499(v=vs.85).aspx +//Note: on Windows it seems impossible to specify which pipes to use +//Thus, if stdin_h, stdout_h and stderr all are NULL, the out,err,in is sent to the parent process instead +process_id_type Process::open(const std::string &command, const std::string &path) { + if(use_stdin) + stdin_fd=std::unique_ptr(new file_descriptor_type); + if(read_stdout) + stdout_fd=std::unique_ptr(new file_descriptor_type); + if(read_stderr) + stderr_fd=std::unique_ptr(new file_descriptor_type); + + HANDLE g_hChildStd_IN_Rd = NULL; + HANDLE g_hChildStd_IN_Wr = NULL; + HANDLE g_hChildStd_OUT_Rd = NULL; + HANDLE g_hChildStd_OUT_Wr = NULL; + HANDLE g_hChildStd_ERR_Rd = NULL; + HANDLE g_hChildStd_ERR_Wr = NULL; + + SECURITY_ATTRIBUTES saAttr; + + saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); + saAttr.bInheritHandle = TRUE; + saAttr.lpSecurityDescriptor = NULL; + + if(stdin_fd) { + if (!CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0)) + return NULL; + if(!SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0)) { + CloseHandle(g_hChildStd_IN_Rd); + CloseHandle(g_hChildStd_IN_Wr); + return NULL; + } + } + if(stdout_fd) { + if (!CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0)) { + if(stdin_fd) CloseHandle(g_hChildStd_IN_Rd); + if(stdin_fd) CloseHandle(g_hChildStd_IN_Wr); + return NULL; + } + if(!SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0)) { + if(stdin_fd) CloseHandle(g_hChildStd_IN_Rd); + if(stdin_fd) CloseHandle(g_hChildStd_IN_Wr); + CloseHandle(g_hChildStd_OUT_Rd); + CloseHandle(g_hChildStd_OUT_Wr); + return NULL; + } + } + if(stderr_fd) { + if (!CreatePipe(&g_hChildStd_ERR_Rd, &g_hChildStd_ERR_Wr, &saAttr, 0)) { + if(stdin_fd) CloseHandle(g_hChildStd_IN_Rd); + if(stdin_fd) CloseHandle(g_hChildStd_IN_Wr); + if(stdout_fd) CloseHandle(g_hChildStd_OUT_Rd); + if(stdout_fd) CloseHandle(g_hChildStd_OUT_Wr); + return NULL; + } + if(!SetHandleInformation(g_hChildStd_ERR_Rd, HANDLE_FLAG_INHERIT, 0)) { + if(stdin_fd) CloseHandle(g_hChildStd_IN_Rd); + if(stdin_fd) CloseHandle(g_hChildStd_IN_Wr); + if(stdout_fd) CloseHandle(g_hChildStd_OUT_Rd); + if(stdout_fd) CloseHandle(g_hChildStd_OUT_Wr); + CloseHandle(g_hChildStd_ERR_Rd); + CloseHandle(g_hChildStd_ERR_Wr); + return NULL; + } + } + + PROCESS_INFORMATION process_info; + STARTUPINFO siStartInfo; + + ZeroMemory(&process_info, sizeof(PROCESS_INFORMATION)); + + ZeroMemory(&siStartInfo, sizeof(STARTUPINFO)); + siStartInfo.cb = sizeof(STARTUPINFO); + if(stdin_fd) siStartInfo.hStdInput = g_hChildStd_IN_Rd; + if(stdout_fd) siStartInfo.hStdOutput = g_hChildStd_OUT_Wr; + if(stderr_fd) siStartInfo.hStdError = g_hChildStd_ERR_Wr; + if(stdin_fd || stdout_fd || stderr_fd) + siStartInfo.dwFlags |= STARTF_USESTDHANDLES; + + char* path_ptr; + if(path=="") + path_ptr=NULL; + else { + path_ptr=new char[path.size()+1]; + std::strcpy(path_ptr, path.c_str()); + } + char* command_cstr=new char[command.size()+1]; + std::strcpy(command_cstr, command.c_str()); + BOOL bSuccess = CreateProcess(NULL, + command_cstr, // command line + NULL, // process security attributes + NULL, // primary thread security attributes + TRUE, // handles are inherited + 0, // creation flags + NULL, // use parent's environment + path_ptr, // use parent's current directory + &siStartInfo, // STARTUPINFO pointer + &process_info); // receives PROCESS_INFORMATION + + if(!bSuccess) { + CloseHandle(process_info.hProcess); + CloseHandle(process_info.hThread); + if(stdin_fd) CloseHandle(g_hChildStd_IN_Rd); + if(stdout_fd) CloseHandle(g_hChildStd_OUT_Wr); + if(stderr_fd) CloseHandle(g_hChildStd_ERR_Wr); + return NULL; + } + else { + // Close handles to the child process and its primary thread. + // Some applications might keep these handles to monitor the status + // of the child process, for example. + + CloseHandle(process_info.hThread); + if(stdin_fd) CloseHandle(g_hChildStd_IN_Rd); + if(stdout_fd) CloseHandle(g_hChildStd_OUT_Wr); + if(stderr_fd) CloseHandle(g_hChildStd_ERR_Wr); + } + + if(stdin_fd) *stdin_fd=g_hChildStd_IN_Wr; + if(stdout_fd) *stdout_fd=g_hChildStd_OUT_Rd; + if(stderr_fd) *stderr_fd=g_hChildStd_ERR_Rd; + return process_info.hProcess; +} + +void Process::async_read() { + if(stdout_fd) { + stdout_thread=std::thread([this](){ + DWORD n; + char buffer[buffer_size]; + for (;;) { + BOOL bSuccess = ReadFile(*stdout_fd, static_cast(buffer), static_cast(buffer_size), &n, NULL); + if(!bSuccess || n == 0) + break; + read_stdout(buffer, static_cast(n)); + } + }); + } + if(stderr_fd) { + stderr_thread=std::thread([this](){ + DWORD n; + char buffer[buffer_size]; + for (;;) { + BOOL bSuccess = ReadFile(*stderr_fd, static_cast(buffer), static_cast(buffer_size), &n, NULL); + if(!bSuccess || n == 0) + break; + read_stderr(buffer, static_cast(n)); + } + }); + } +} + +int Process::get_exit_code() { + DWORD exit_code; + WaitForSingleObject(id, INFINITE); + GetExitCodeProcess(id, &exit_code); + CloseHandle(id); + + if(stdout_thread.joinable()) + stdout_thread.join(); + if(stderr_thread.joinable()) + stderr_thread.join(); + + stdin_mutex.lock(); + if(stdin_fd) { + CloseHandle(*stdin_fd); + stdin_fd.reset(); + } + stdin_mutex.unlock(); + if(stdout_fd) { + CloseHandle(*stdout_fd); + stdout_fd.reset(); + } + if(stderr_fd) { + CloseHandle(*stderr_fd); + stderr_fd.reset(); + } + + return static_cast(exit_code); +} + +bool Process::write(const char *bytes, size_t n) { + stdin_mutex.lock(); + if(stdin_fd) { + DWORD written; + BOOL bSuccess=WriteFile(id, bytes, static_cast(n), &written, NULL); + if(!bSuccess || written==0) { + stdin_mutex.unlock(); + return false; + } + else { + stdin_mutex.unlock(); + return true; + } + } + stdin_mutex.unlock(); + return false; +} + +void Process::kill(process_id_type id, bool force) { + TerminateProcess(id, 2); +} diff --git a/src/terminal.cc b/src/terminal.cc index e9c8d42..a8cc032 100644 --- a/src/terminal.cc +++ b/src/terminal.cc @@ -144,7 +144,7 @@ void Terminal::async_process(const std::string &command, const boost::filesystem processes_mutex.unlock(); } - exit_code_type exit_code=process->get_exit_code(); + auto exit_code=process->get_exit_code(); processes_mutex.lock(); for(auto it=processes.begin();it!=processes.end();it++) { diff --git a/src/terminal.h b/src/terminal.h index c122b36..2ab0fe8 100644 --- a/src/terminal.h +++ b/src/terminal.h @@ -29,7 +29,7 @@ public: Terminal(); int process(const std::string &command, const boost::filesystem::path &path="", bool use_pipes=true); int process(std::istream &stdin_stream, std::ostream &stdout_stream, const std::string &command, const boost::filesystem::path &path=""); - void async_process(const std::string &command, const boost::filesystem::path &path="", std::function callback=nullptr); + void async_process(const std::string &command, const boost::filesystem::path &path="", std::function callback=nullptr); void kill_last_async_process(bool force=false); void kill_async_processes(bool force=false);