Merge pull request #5714 from alexischr/update_bockbuild
[mono.git] / mono / metadata / w32process-win32.c
1 /**
2  * \file
3  * System.Diagnostics.Process support
4  *
5  * Author:
6  *      Dick Porter (dick@ximian.com)
7  *
8  * Copyright 2002 Ximian, Inc.
9  * Copyright 2002-2006 Novell, Inc.
10  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
11  */
12
13 #include <config.h>
14
15 #include <glib.h>
16 #include <string.h>
17
18 #include <winsock2.h>
19 #include <windows.h>
20
21 #include <mono/metadata/object-internals.h>
22 #include <mono/metadata/w32process.h>
23 #include <mono/metadata/w32process-win32-internals.h>
24 #include <mono/metadata/assembly.h>
25 #include <mono/metadata/appdomain.h>
26 #include <mono/metadata/image.h>
27 #include <mono/metadata/cil-coff.h>
28 #include <mono/metadata/exception.h>
29 #include <mono/metadata/threadpool-io.h>
30 #include <mono/utils/strenc.h>
31 #include <mono/utils/mono-proclib.h>
32 /* FIXME: fix this code to not depend so much on the internals */
33 #include <mono/metadata/class-internals.h>
34 #include <mono/metadata/w32handle.h>
35 #include <mono/utils/w32api.h>
36
37 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
38 #include <shellapi.h>
39 #endif
40
41 void
42 mono_w32process_init (void)
43 {
44 }
45
46 void
47 mono_w32process_cleanup (void)
48 {
49 }
50
51 void
52 mono_w32process_signal_finished (void)
53 {
54 }
55
56 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
57 HANDLE
58 ves_icall_System_Diagnostics_Process_GetProcess_internal (guint32 pid)
59 {
60         HANDLE handle;
61         
62         /* GetCurrentProcess returns a pseudo-handle, so use
63          * OpenProcess instead
64          */
65         handle = OpenProcess (PROCESS_ALL_ACCESS, TRUE, pid);
66         if (handle == NULL)
67                 /* FIXME: Throw an exception */
68                 return NULL;
69         return handle;
70 }
71 #endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT) */
72
73 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
74 MonoBoolean
75 ves_icall_System_Diagnostics_Process_ShellExecuteEx_internal (MonoW32ProcessStartInfo *proc_start_info, MonoW32ProcessInfo *process_info)
76 {
77         SHELLEXECUTEINFO shellex = {0};
78         gboolean ret;
79
80         shellex.cbSize = sizeof(SHELLEXECUTEINFO);
81         shellex.fMask = (gulong)(SEE_MASK_FLAG_DDEWAIT | SEE_MASK_NOCLOSEPROCESS | SEE_MASK_UNICODE);
82         shellex.nShow = (gulong)proc_start_info->window_style;
83         shellex.nShow = (gulong)((shellex.nShow == 0) ? 1 : (shellex.nShow == 1 ? 0 : shellex.nShow));
84
85         if (proc_start_info->filename != NULL) {
86                 shellex.lpFile = mono_string_chars (proc_start_info->filename);
87         }
88
89         if (proc_start_info->arguments != NULL) {
90                 shellex.lpParameters = mono_string_chars (proc_start_info->arguments);
91         }
92
93         if (proc_start_info->verb != NULL &&
94             mono_string_length (proc_start_info->verb) != 0) {
95                 shellex.lpVerb = mono_string_chars (proc_start_info->verb);
96         }
97
98         if (proc_start_info->working_directory != NULL &&
99             mono_string_length (proc_start_info->working_directory) != 0) {
100                 shellex.lpDirectory = mono_string_chars (proc_start_info->working_directory);
101         }
102
103         if (proc_start_info->error_dialog) {    
104                 shellex.hwnd = proc_start_info->error_dialog_parent_handle;
105         } else {
106                 shellex.fMask = (gulong)(shellex.fMask | SEE_MASK_FLAG_NO_UI);
107         }
108
109         ret = ShellExecuteEx (&shellex);
110         if (ret == FALSE) {
111                 process_info->pid = -GetLastError ();
112         } else {
113                 process_info->process_handle = shellex.hProcess;
114                 process_info->thread_handle = NULL;
115 #if !defined(MONO_CROSS_COMPILE)
116                 process_info->pid = GetProcessId (shellex.hProcess);
117 #else
118                 process_info->pid = 0;
119 #endif
120                 process_info->tid = 0;
121         }
122
123         return ret;
124 }
125 #endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */
126
127 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
128 static inline void
129 mono_process_init_startup_info (HANDLE stdin_handle, HANDLE stdout_handle, HANDLE stderr_handle, STARTUPINFO *startinfo)
130 {
131         startinfo->cb = sizeof(STARTUPINFO);
132         startinfo->dwFlags = STARTF_USESTDHANDLES;
133         startinfo->hStdInput = stdin_handle;
134         startinfo->hStdOutput = stdout_handle;
135         startinfo->hStdError = stderr_handle;
136         return;
137 }
138
139 static gboolean
140 mono_process_create_process (MonoW32ProcessInfo *mono_process_info, MonoString *cmd, guint32 creation_flags,
141         gunichar2 *env_vars, gunichar2 *dir, STARTUPINFO *start_info, PROCESS_INFORMATION *process_info)
142 {
143         gboolean result = FALSE;
144
145         if (mono_process_info->username) {
146                 guint32 logon_flags = mono_process_info->load_user_profile ? LOGON_WITH_PROFILE : 0;
147
148                 result = CreateProcessWithLogonW (mono_string_chars (mono_process_info->username),
149                                                   mono_process_info->domain ? mono_string_chars (mono_process_info->domain) : NULL,
150                                                   (const gunichar2 *)mono_process_info->password,
151                                                   logon_flags,
152                                                   NULL,
153                                                   cmd ? mono_string_chars (cmd) : NULL,
154                                                   creation_flags,
155                                                   env_vars, dir, start_info, process_info);
156
157         } else {
158
159                 result = CreateProcessW (NULL,
160                                         cmd ? mono_string_chars (cmd): NULL,
161                                         NULL,
162                                         NULL,
163                                         TRUE,
164                                         creation_flags,
165                                         env_vars,
166                                         dir,
167                                         start_info,
168                                         process_info);
169
170         }
171
172         return result;
173 }
174 #endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */
175
176 static gchar*
177 process_unquote_application_name (gchar *appname)
178 {
179         size_t len = strlen (appname);
180         if (len) {
181                 if (appname[len-1] == '\"')
182                         appname[len-1] = '\0';
183                 if (appname[0] == '\"')
184                         appname++;
185         }
186
187         return appname;
188 }
189
190 static gchar*
191 process_quote_path (const gchar *path)
192 {
193         gchar *res = g_shell_quote (path);
194         gchar *q = res;
195         while (*q) {
196                 if (*q == '\'')
197                         *q = '\"';
198                 q++;
199         }
200         return res;
201 }
202
203 /* Only used when UseShellExecute is false */
204 static gboolean
205 process_complete_path (const gunichar2 *appname, gchar **completed)
206 {
207         gchar *utf8app, *utf8appmemory;
208         gchar *found;
209
210         utf8appmemory = g_utf16_to_utf8 (appname, -1, NULL, NULL, NULL);
211         utf8app = process_unquote_application_name (utf8appmemory);
212
213         if (g_path_is_absolute (utf8app)) {
214                 *completed = process_quote_path (utf8app);
215                 g_free (utf8appmemory);
216                 return TRUE;
217         }
218
219         if (g_file_test (utf8app, G_FILE_TEST_IS_EXECUTABLE) && !g_file_test (utf8app, G_FILE_TEST_IS_DIR)) {
220                 *completed = process_quote_path (utf8app);
221                 g_free (utf8appmemory);
222                 return TRUE;
223         }
224         
225         found = g_find_program_in_path (utf8app);
226         if (found == NULL) {
227                 *completed = NULL;
228                 g_free (utf8appmemory);
229                 return FALSE;
230         }
231
232         *completed = process_quote_path (found);
233         g_free (found);
234         g_free (utf8appmemory);
235         return TRUE;
236 }
237
238 static gboolean
239 process_get_shell_arguments (MonoW32ProcessStartInfo *proc_start_info, MonoString **cmd)
240 {
241         gchar           *spath = NULL;
242         gchar           *new_cmd, *cmd_utf8;
243         MonoError       mono_error;
244
245         *cmd = proc_start_info->arguments;
246
247         if (process_complete_path (mono_string_chars (proc_start_info->filename), &spath)) {
248                 /* Seems like our CreateProcess does not work as the windows one.
249                  * This hack is needed to deal with paths containing spaces */
250                 if (*cmd) {
251                         cmd_utf8 = mono_string_to_utf8_checked (*cmd, &mono_error);
252                         if (!mono_error_set_pending_exception (&mono_error)) {
253                                 new_cmd = g_strdup_printf ("%s %s", spath, cmd_utf8);
254                                 *cmd = mono_string_new_wrapper (new_cmd);
255                                 g_free (cmd_utf8);
256                                 g_free (new_cmd);
257                         } else {
258                                 *cmd = NULL;
259                         }
260                 }
261                 else {
262                         *cmd = mono_string_new_wrapper (spath);
263                 }
264
265                 g_free (spath);
266         }
267
268         return (*cmd != NULL) ? TRUE : FALSE;
269 }
270
271 MonoBoolean
272 ves_icall_System_Diagnostics_Process_CreateProcess_internal (MonoW32ProcessStartInfo *proc_start_info, HANDLE stdin_handle,
273                                                              HANDLE stdout_handle, HANDLE stderr_handle, MonoW32ProcessInfo *process_info)
274 {
275         gboolean ret;
276         gunichar2 *dir;
277         STARTUPINFO startinfo={0};
278         PROCESS_INFORMATION procinfo;
279         gunichar2 *env_vars = NULL;
280         MonoString *cmd = NULL;
281         guint32 creation_flags;
282
283         mono_process_init_startup_info (stdin_handle, stdout_handle, stderr_handle, &startinfo);
284
285         creation_flags = CREATE_UNICODE_ENVIRONMENT;
286         if (proc_start_info->create_no_window)
287                 creation_flags |= CREATE_NO_WINDOW;
288         
289         if (process_get_shell_arguments (proc_start_info, &cmd) == FALSE) {
290                 process_info->pid = -ERROR_FILE_NOT_FOUND;
291                 return FALSE;
292         }
293
294         if (process_info->env_variables) {
295                 gint i, len;
296                 MonoString *var;
297                 gunichar2 *str, *ptr;
298
299                 len = 0;
300
301                 for (i = 0; i < mono_array_length (process_info->env_variables); i++) {
302                         var = mono_array_get (process_info->env_variables, MonoString*, i);
303
304                         len += mono_string_length (var) * sizeof (gunichar2);
305
306                         /* null-separated */
307                         len += sizeof (gunichar2);
308                 }
309                 /* null-terminated */
310                 len += sizeof (gunichar2);
311
312                 env_vars = ptr = g_new0 (gunichar2, len);
313
314                 for (i = 0; i < mono_array_length (process_info->env_variables); i++) {
315                         var = mono_array_get (process_info->env_variables, MonoString*, i);
316
317                         memcpy (ptr, mono_string_chars (var), mono_string_length (var) * sizeof (gunichar2));
318                         ptr += mono_string_length (var);
319                         ptr += 1; // Skip over the null-separator
320                 }
321         }
322         
323         /* The default dir name is "".  Turn that into NULL to mean
324          * "current directory"
325          */
326         if (proc_start_info->working_directory == NULL || mono_string_length (proc_start_info->working_directory) == 0)
327                 dir = NULL;
328         else
329                 dir = mono_string_chars (proc_start_info->working_directory);
330
331         ret = mono_process_create_process (process_info, cmd, creation_flags, env_vars, dir, &startinfo, &procinfo);
332
333         g_free (env_vars);
334
335         if (ret) {
336                 process_info->process_handle = procinfo.hProcess;
337                 /*process_info->thread_handle=procinfo.hThread;*/
338                 process_info->thread_handle = NULL;
339                 if (procinfo.hThread != NULL && procinfo.hThread != INVALID_HANDLE_VALUE)
340                         CloseHandle (procinfo.hThread);
341                 process_info->pid = procinfo.dwProcessId;
342                 process_info->tid = procinfo.dwThreadId;
343         } else {
344                 process_info->pid = -GetLastError ();
345         }
346         
347         return ret;
348 }
349
350 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
351 static inline gboolean
352 mono_process_win_enum_processes (DWORD *pids, DWORD count, DWORD *needed)
353 {
354         return EnumProcesses (pids, count, needed);
355 }
356 #endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */
357
358 MonoArray *
359 ves_icall_System_Diagnostics_Process_GetProcesses_internal (void)
360 {
361         MonoError error;
362         MonoArray *procs;
363         gboolean ret;
364         DWORD needed;
365         int count;
366         DWORD *pids;
367
368         count = 512;
369         do {
370                 pids = g_new0 (DWORD, count);
371                 ret = mono_process_win_enum_processes (pids, count * sizeof (guint32), &needed);
372                 if (ret == FALSE) {
373                         MonoException *exc;
374
375                         g_free (pids);
376                         pids = NULL;
377                         exc = mono_get_exception_not_supported ("This system does not support EnumProcesses");
378                         mono_set_pending_exception (exc);
379                         return NULL;
380                 }
381                 if (needed < (count * sizeof (guint32)))
382                         break;
383                 g_free (pids);
384                 pids = NULL;
385                 count = (count * 3) / 2;
386         } while (TRUE);
387
388         count = needed / sizeof (guint32);
389         procs = mono_array_new_checked (mono_domain_get (), mono_get_int32_class (), count, &error);
390         if (mono_error_set_pending_exception (&error)) {
391                 g_free (pids);
392                 return NULL;
393         }
394
395         memcpy (mono_array_addr (procs, guint32, 0), pids, needed);
396         g_free (pids);
397         pids = NULL;
398
399         return procs;
400 }
401
402 MonoBoolean
403 ves_icall_Microsoft_Win32_NativeMethods_CloseProcess (gpointer handle)
404 {
405         return CloseHandle (handle);
406 }
407
408 MonoBoolean
409 ves_icall_Microsoft_Win32_NativeMethods_TerminateProcess (gpointer handle, gint32 exitcode)
410 {
411         return TerminateProcess (handle, exitcode);
412 }
413
414 MonoBoolean
415 ves_icall_Microsoft_Win32_NativeMethods_GetExitCodeProcess (gpointer handle, gint32 *exitcode)
416 {
417         return GetExitCodeProcess (handle, exitcode);
418 }
419
420 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
421 static inline MonoBoolean
422 mono_icall_get_process_working_set_size (gpointer handle, gsize *min, gsize *max)
423 {
424         return GetProcessWorkingSetSize (handle, min, max);
425 }
426 #endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */
427
428 MonoBoolean
429 ves_icall_Microsoft_Win32_NativeMethods_GetProcessWorkingSetSize (gpointer handle, gsize *min, gsize *max)
430 {
431         return mono_icall_get_process_working_set_size (handle, min, max);
432 }
433
434 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
435 static inline MonoBoolean
436 mono_icall_set_process_working_set_size (gpointer handle, gsize min, gsize max)
437 {
438         return SetProcessWorkingSetSize (handle, min, max);
439 }
440 #endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */
441
442 MonoBoolean
443 ves_icall_Microsoft_Win32_NativeMethods_SetProcessWorkingSetSize (gpointer handle, gsize min, gsize max)
444 {
445         return mono_icall_set_process_working_set_size (handle, min, max);
446 }
447
448 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
449 static inline gint32
450 mono_icall_get_priority_class (gpointer handle)
451 {
452         return GetPriorityClass (handle);
453 }
454 #endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */
455
456 gint32
457 ves_icall_Microsoft_Win32_NativeMethods_GetPriorityClass (gpointer handle)
458 {
459         return mono_icall_get_priority_class (handle);
460 }
461
462 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
463 static inline MonoBoolean
464 mono_icall_set_priority_class (gpointer handle, gint32 priorityClass)
465 {
466         return SetPriorityClass (handle, (guint32) priorityClass);
467 }
468 #endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */
469
470 MonoBoolean
471 ves_icall_Microsoft_Win32_NativeMethods_SetPriorityClass (gpointer handle, gint32 priorityClass)
472 {
473         return mono_icall_set_priority_class (handle, priorityClass);
474 }
475
476 MonoBoolean
477 ves_icall_Microsoft_Win32_NativeMethods_GetProcessTimes (gpointer handle, gint64 *creationtime, gint64 *exittime, gint64 *kerneltime, gint64 *usertime)
478 {
479         return GetProcessTimes (handle, (LPFILETIME) creationtime, (LPFILETIME) exittime, (LPFILETIME) kerneltime, (LPFILETIME) usertime);
480 }
481
482 gpointer
483 ves_icall_Microsoft_Win32_NativeMethods_GetCurrentProcess (void)
484 {
485         return GetCurrentProcess ();
486 }