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