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