2 * process.c: System.Diagnostics.Process support
5 * Dick Porter (dick@ximian.com)
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.
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>
36 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
41 mono_w32process_init (void)
46 mono_w32process_cleanup (void)
50 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
52 ves_icall_System_Diagnostics_Process_GetProcess_internal (guint32 pid)
56 /* GetCurrentProcess returns a pseudo-handle, so use
59 handle = OpenProcess (PROCESS_ALL_ACCESS, TRUE, pid);
61 /* FIXME: Throw an exception */
65 #endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT) */
68 mono_process_unquote_application_name (gchar *appname)
70 size_t len = strlen (appname);
72 if (appname[len-1] == '\"')
73 appname[len-1] = '\0';
74 if (appname[0] == '\"')
82 mono_process_quote_path (const gchar *path)
84 gchar *res = g_shell_quote (path);
94 /* Only used when UseShellExecute is false */
96 mono_process_complete_path (const gunichar2 *appname, gchar **completed)
98 gchar *utf8app, *utf8appmemory;
101 utf8appmemory = g_utf16_to_utf8 (appname, -1, NULL, NULL, NULL);
102 utf8app = mono_process_unquote_application_name (utf8appmemory);
104 if (g_path_is_absolute (utf8app)) {
105 *completed = mono_process_quote_path (utf8app);
106 g_free (utf8appmemory);
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);
116 found = g_find_program_in_path (utf8app);
119 g_free (utf8appmemory);
123 *completed = mono_process_quote_path (found);
125 g_free (utf8appmemory);
129 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
131 ves_icall_System_Diagnostics_Process_ShellExecuteEx_internal (MonoW32ProcessStartInfo *proc_start_info, MonoW32ProcessInfo *process_info)
133 SHELLEXECUTEINFO shellex = {0};
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));
141 if (proc_start_info->filename != NULL) {
142 shellex.lpFile = mono_string_chars (proc_start_info->filename);
145 if (proc_start_info->arguments != NULL) {
146 shellex.lpParameters = mono_string_chars (proc_start_info->arguments);
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);
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);
159 if (proc_start_info->error_dialog) {
160 shellex.hwnd = proc_start_info->error_dialog_parent_handle;
162 shellex.fMask = (gulong)(shellex.fMask | SEE_MASK_FLAG_NO_UI);
165 ret = ShellExecuteEx (&shellex);
167 process_info->pid = -GetLastError ();
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);
174 process_info->pid = 0;
176 process_info->tid = 0;
181 #endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */
183 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
185 mono_process_init_startup_info (HANDLE stdin_handle, HANDLE stdout_handle, HANDLE stderr_handle, STARTUPINFO *startinfo)
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;
194 #endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */
196 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
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)
202 gboolean result = FALSE;
204 if (mono_process_info->username) {
205 guint32 logon_flags = mono_process_info->load_user_profile ? LOGON_WITH_PROFILE : 0;
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,
212 cmd ? mono_string_chars (cmd) : NULL,
214 env_vars, dir, start_info, process_info);
218 result = CreateProcess (shell_path,
219 cmd ? mono_string_chars (cmd): NULL,
233 #endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */
236 mono_process_get_shell_arguments (MonoW32ProcessStartInfo *proc_start_info, gunichar2 **shell_path, MonoString **cmd)
239 gchar *new_cmd, *cmd_utf8;
240 MonoError mono_error;
243 *cmd = proc_start_info->arguments;
245 mono_process_complete_path (mono_string_chars (proc_start_info->filename), &spath);
247 /* Seems like our CreateProcess does not work as the windows one.
248 * This hack is needed to deal with paths containing spaces */
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);
261 *cmd = mono_string_new_wrapper (spath);
267 return (*cmd != NULL) ? TRUE : FALSE;
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)
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;
283 mono_process_init_startup_info (stdin_handle, stdout_handle, stderr_handle, &startinfo);
285 creation_flags = CREATE_UNICODE_ENVIRONMENT;
286 if (proc_start_info->create_no_window)
287 creation_flags |= CREATE_NO_WINDOW;
289 if (mono_process_get_shell_arguments (proc_start_info, &shell_path, &cmd) == FALSE) {
290 process_info->pid = -ERROR_FILE_NOT_FOUND;
294 if (process_info->env_keys) {
297 MonoString *key, *value;
298 gunichar2 *str, *ptr;
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);
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);
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);
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);
323 memcpy (ptr, equals16, sizeof (gunichar2));
326 memcpy (ptr, mono_string_chars (value), mono_string_length (value) * sizeof (gunichar2));
327 ptr += mono_string_length (value);
332 env_vars = (gchar *) str;
335 /* The default dir name is "". Turn that into NULL to mean
336 * "current directory"
338 if (proc_start_info->working_directory == NULL || mono_string_length (proc_start_info->working_directory) == 0)
341 dir = mono_string_chars (proc_start_info->working_directory);
343 ret = mono_process_create_process (process_info, shell_path, cmd, creation_flags, env_vars, dir, &startinfo, &procinfo);
346 if (shell_path != NULL)
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;
358 process_info->pid = -GetLastError ();
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)
368 return EnumProcesses (pids, count, needed);
370 #endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */
373 ves_icall_System_Diagnostics_Process_GetProcesses_internal (void)
384 pids = g_new0 (DWORD, count);
385 ret = mono_process_win_enum_processes (pids, count * sizeof (guint32), &needed);
391 exc = mono_get_exception_not_supported ("This system does not support EnumProcesses");
392 mono_set_pending_exception (exc);
395 if (needed < (count * sizeof (guint32)))
399 count = (count * 3) / 2;
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)) {
409 memcpy (mono_array_addr (procs, guint32, 0), pids, needed);
417 ves_icall_Microsoft_Win32_NativeMethods_CloseProcess (gpointer handle)
419 return CloseHandle (handle);
423 ves_icall_Microsoft_Win32_NativeMethods_TerminateProcess (gpointer handle, gint32 exitcode)
425 return TerminateProcess (handle, exitcode);
429 ves_icall_Microsoft_Win32_NativeMethods_GetExitCodeProcess (gpointer handle, gint32 *exitcode)
431 return GetExitCodeProcess (handle, exitcode);
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)
438 return GetProcessWorkingSetSize (handle, min, max);
440 #endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */
443 ves_icall_Microsoft_Win32_NativeMethods_GetProcessWorkingSetSize (gpointer handle, gsize *min, gsize *max)
445 return mono_icall_get_process_working_set_size (handle, min, max);
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)
452 return SetProcessWorkingSetSize (handle, min, max);
454 #endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */
457 ves_icall_Microsoft_Win32_NativeMethods_SetProcessWorkingSetSize (gpointer handle, gsize min, gsize max)
459 return mono_icall_set_process_working_set_size (handle, min, max);
462 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
464 mono_icall_get_priority_class (gpointer handle)
466 return GetPriorityClass (handle);
468 #endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */
471 ves_icall_Microsoft_Win32_NativeMethods_GetPriorityClass (gpointer handle)
473 return mono_icall_get_priority_class (handle);
476 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
477 static inline MonoBoolean
478 mono_icall_set_priority_class (gpointer handle, gint32 priorityClass)
480 return SetPriorityClass (handle, (guint32) priorityClass);
482 #endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */
485 ves_icall_Microsoft_Win32_NativeMethods_SetPriorityClass (gpointer handle, gint32 priorityClass)
487 return mono_icall_set_priority_class (handle, priorityClass);
491 ves_icall_Microsoft_Win32_NativeMethods_GetProcessTimes (gpointer handle, gint64 *creationtime, gint64 *exittime, gint64 *kerneltime, gint64 *usertime)
493 return GetProcessTimes (handle, (LPFILETIME) creationtime, (LPFILETIME) exittime, (LPFILETIME) kerneltime, (LPFILETIME) usertime);
497 ves_icall_Microsoft_Win32_NativeMethods_GetCurrentProcess (void)
499 return GetCurrentProcess ();