]> git.tdb.fi Git - libs/core.git/blob - source/core/windows/process.cpp
Add new exception classes for some common errors
[libs/core.git] / source / core / windows / process.cpp
1 #include <windows.h>
2 #include <msp/core/systemerror.h>
3 #include <msp/io/handle_private.h>
4 #include <msp/strings/utils.h>
5 #include "except.h"
6 #include "process.h"
7 #include "process_private.h"
8
9 using namespace std;
10
11 namespace {
12
13 string quote_argument(const string &arg)
14 {
15         string result;
16         bool need_quotes = false;
17         bool backslash = false;
18         for(char c: arg)
19         {
20                 if(c=='\\')
21                         backslash = true;
22                 else if(c=='"')
23                 {
24                         if(backslash)
25                                 result += '\\';
26                         result += '\\';
27                 }
28                 else if(c==' ')
29                         need_quotes = true;
30                 result += c;
31         }
32
33         if(need_quotes)
34                 return "\""+result+"\"";
35         else
36                 return result;
37 }
38
39 }
40
41 namespace Msp {
42
43 Process::~Process()
44 {
45         CloseHandle(priv->info.hProcess);
46         CloseHandle(priv->info.hThread);
47 }
48
49 void Process::platform_get_self_info(Private &priv)
50 {
51         priv.info.hProcess = GetCurrentProcess();
52         priv.info.hThread = nullptr;
53         priv.info.dwProcessId = GetCurrentProcessId();
54         priv.info.dwThreadId = 0;
55 }
56
57 void Process::execute(const string &command, bool path_search, const Arguments &args)
58 {
59         string cmdline = quote_argument(command);
60         for(const string &a: args)
61                 append(cmdline, " ", quote_argument(a));
62
63         STARTUPINFO startup;
64         startup.cb = sizeof(STARTUPINFO);
65         startup.lpReserved = nullptr;
66         startup.lpDesktop = nullptr;
67         startup.lpTitle = nullptr;
68         startup.dwFlags = 0;
69         startup.cbReserved2 = 0;
70         startup.lpReserved2 = nullptr;
71         if(redirect)
72         {
73                 startup.dwFlags |= STARTF_USESTDHANDLES;
74                 HANDLE self_handle = self().priv->info.hProcess;
75                 HANDLE cin_handle = (cin ? *cin->get_handle(IO::M_READ) : GetStdHandle(STD_INPUT_HANDLE));
76                 DuplicateHandle(self_handle, cin_handle, self_handle, &startup.hStdInput, 0, true, DUPLICATE_SAME_ACCESS);
77                 HANDLE cout_handle = (cout ? *cout->get_handle(IO::M_WRITE) : GetStdHandle(STD_OUTPUT_HANDLE));
78                 DuplicateHandle(self_handle, cout_handle, self_handle, &startup.hStdOutput, 0, true, DUPLICATE_SAME_ACCESS);
79                 HANDLE cerr_handle = (cerr ? *cerr->get_handle(IO::M_WRITE) : GetStdHandle(STD_ERROR_HANDLE));
80                 DuplicateHandle(self_handle, cerr_handle, self_handle, &startup.hStdError, 0, true, DUPLICATE_SAME_ACCESS);
81         }
82         const char *cmdptr = (path_search ? nullptr : command.c_str());
83         const char *wd = (work_dir.empty() ? nullptr : work_dir.c_str());
84         if(!CreateProcess(cmdptr, const_cast<char *>(cmdline.c_str()), nullptr, nullptr, true, 0, nullptr, wd, &startup, &priv->info))
85                 throw system_error("CreateProcess");
86
87         if(redirect)
88         {
89                 CloseHandle(startup.hStdInput);
90                 CloseHandle(startup.hStdOutput);
91                 CloseHandle(startup.hStdError);
92         }
93
94         running = true;
95
96         if(this==_self)
97                 TerminateProcess(priv->info.hProcess, 0);
98 }
99
100 bool Process::wait(bool block)
101 {
102         if(!running)
103                 throw invalid_state("not running");
104
105         DWORD ret = WaitForSingleObject(priv->info.hProcess, (block ? INFINITE : 0));
106         if(ret==WAIT_FAILED)
107                 throw system_error("WaitForSingleObject");
108
109         if(ret==WAIT_OBJECT_0)
110         {
111                 running = false;
112                 finished = true;
113                 DWORD ec;
114                 if(GetExitCodeProcess(priv->info.hProcess, &ec))
115                         exit_code = ec;
116         }
117
118         return finished;
119 }
120
121 void Process::terminate()
122 {
123         TerminateProcess(priv->info.hProcess, 1);
124 }
125
126 void Process::kill()
127 {
128         TerminateProcess(priv->info.hProcess, 1);
129 }
130
131 void Process::interrupt()
132 {
133         TerminateProcess(priv->info.hProcess, 1);
134 }
135
136
137 Process::Private::Private()
138 {
139         info.hProcess = nullptr;
140         info.hThread = nullptr;
141         info.dwProcessId = 0;
142         info.dwThreadId = 0;
143 }
144
145 } // namespace Msp