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