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-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) */
67 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
69 ves_icall_System_Diagnostics_Process_ShellExecuteEx_internal (MonoW32ProcessStartInfo
*proc_start_info
, MonoW32ProcessInfo
*process_info
)
71 SHELLEXECUTEINFO shellex
= {0};
74 shellex
.cbSize
= sizeof(SHELLEXECUTEINFO
);
75 shellex
.fMask
= (gulong
)(SEE_MASK_FLAG_DDEWAIT
| SEE_MASK_NOCLOSEPROCESS
| SEE_MASK_UNICODE
);
76 shellex
.nShow
= (gulong
)proc_start_info
->window_style
;
77 shellex
.nShow
= (gulong
)((shellex
.nShow
== 0) ? 1 : (shellex
.nShow
== 1 ? 0 : shellex
.nShow
));
79 if (proc_start_info
->filename
!= NULL
) {
80 shellex
.lpFile
= mono_string_chars (proc_start_info
->filename
);
83 if (proc_start_info
->arguments
!= NULL
) {
84 shellex
.lpParameters
= mono_string_chars (proc_start_info
->arguments
);
87 if (proc_start_info
->verb
!= NULL
&&
88 mono_string_length (proc_start_info
->verb
) != 0) {
89 shellex
.lpVerb
= mono_string_chars (proc_start_info
->verb
);
92 if (proc_start_info
->working_directory
!= NULL
&&
93 mono_string_length (proc_start_info
->working_directory
) != 0) {
94 shellex
.lpDirectory
= mono_string_chars (proc_start_info
->working_directory
);
97 if (proc_start_info
->error_dialog
) {
98 shellex
.hwnd
= proc_start_info
->error_dialog_parent_handle
;
100 shellex
.fMask
= (gulong
)(shellex
.fMask
| SEE_MASK_FLAG_NO_UI
);
103 ret
= ShellExecuteEx (&shellex
);
105 process_info
->pid
= -GetLastError ();
107 process_info
->process_handle
= shellex
.hProcess
;
108 process_info
->thread_handle
= NULL
;
109 #if !defined(MONO_CROSS_COMPILE)
110 process_info
->pid
= GetProcessId (shellex
.hProcess
);
112 process_info
->pid
= 0;
114 process_info
->tid
= 0;
119 #endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */
121 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
123 mono_process_init_startup_info (HANDLE stdin_handle
, HANDLE stdout_handle
, HANDLE stderr_handle
, STARTUPINFO
*startinfo
)
125 startinfo
->cb
= sizeof(STARTUPINFO
);
126 startinfo
->dwFlags
= STARTF_USESTDHANDLES
;
127 startinfo
->hStdInput
= stdin_handle
;
128 startinfo
->hStdOutput
= stdout_handle
;
129 startinfo
->hStdError
= stderr_handle
;
134 mono_process_create_process (MonoW32ProcessInfo
*mono_process_info
, MonoString
*cmd
, guint32 creation_flags
,
135 gunichar2
*env_vars
, gunichar2
*dir
, STARTUPINFO
*start_info
, PROCESS_INFORMATION
*process_info
)
137 gboolean result
= FALSE
;
139 if (mono_process_info
->username
) {
140 guint32 logon_flags
= mono_process_info
->load_user_profile
? LOGON_WITH_PROFILE
: 0;
142 result
= CreateProcessWithLogonW (mono_string_chars (mono_process_info
->username
),
143 mono_process_info
->domain
? mono_string_chars (mono_process_info
->domain
) : NULL
,
144 (const gunichar2
*)mono_process_info
->password
,
147 cmd
? mono_string_chars (cmd
) : NULL
,
149 env_vars
, dir
, start_info
, process_info
);
153 result
= CreateProcessW (NULL
,
154 cmd
? mono_string_chars (cmd
): NULL
,
168 #endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */
171 process_unquote_application_name (gchar
*appname
)
173 size_t len
= strlen (appname
);
175 if (appname
[len
-1] == '\"')
176 appname
[len
-1] = '\0';
177 if (appname
[0] == '\"')
185 process_quote_path (const gchar
*path
)
187 gchar
*res
= g_shell_quote (path
);
197 /* Only used when UseShellExecute is false */
199 process_complete_path (const gunichar2
*appname
, gchar
**completed
)
201 gchar
*utf8app
, *utf8appmemory
;
204 utf8appmemory
= g_utf16_to_utf8 (appname
, -1, NULL
, NULL
, NULL
);
205 utf8app
= process_unquote_application_name (utf8appmemory
);
207 if (g_path_is_absolute (utf8app
)) {
208 *completed
= process_quote_path (utf8app
);
209 g_free (utf8appmemory
);
213 if (g_file_test (utf8app
, G_FILE_TEST_IS_EXECUTABLE
) && !g_file_test (utf8app
, G_FILE_TEST_IS_DIR
)) {
214 *completed
= process_quote_path (utf8app
);
215 g_free (utf8appmemory
);
219 found
= g_find_program_in_path (utf8app
);
222 g_free (utf8appmemory
);
226 *completed
= process_quote_path (found
);
228 g_free (utf8appmemory
);
233 process_get_shell_arguments (MonoW32ProcessStartInfo
*proc_start_info
, MonoString
**cmd
)
236 gchar
*new_cmd
, *cmd_utf8
;
237 MonoError mono_error
;
239 *cmd
= proc_start_info
->arguments
;
241 if (process_complete_path (mono_string_chars (proc_start_info
->filename
), &spath
)) {
242 /* Seems like our CreateProcess does not work as the windows one.
243 * This hack is needed to deal with paths containing spaces */
245 cmd_utf8
= mono_string_to_utf8_checked (*cmd
, &mono_error
);
246 if (!mono_error_set_pending_exception (&mono_error
)) {
247 new_cmd
= g_strdup_printf ("%s %s", spath
, cmd_utf8
);
248 *cmd
= mono_string_new_wrapper (new_cmd
);
256 *cmd
= mono_string_new_wrapper (spath
);
262 return (*cmd
!= NULL
) ? TRUE
: FALSE
;
266 ves_icall_System_Diagnostics_Process_CreateProcess_internal (MonoW32ProcessStartInfo
*proc_start_info
, HANDLE stdin_handle
,
267 HANDLE stdout_handle
, HANDLE stderr_handle
, MonoW32ProcessInfo
*process_info
)
271 STARTUPINFO startinfo
={0};
272 PROCESS_INFORMATION procinfo
;
273 gunichar2
*env_vars
= NULL
;
274 MonoString
*cmd
= NULL
;
275 guint32 creation_flags
;
277 mono_process_init_startup_info (stdin_handle
, stdout_handle
, stderr_handle
, &startinfo
);
279 creation_flags
= CREATE_UNICODE_ENVIRONMENT
;
280 if (proc_start_info
->create_no_window
)
281 creation_flags
|= CREATE_NO_WINDOW
;
283 if (process_get_shell_arguments (proc_start_info
, &cmd
) == FALSE
) {
284 process_info
->pid
= -ERROR_FILE_NOT_FOUND
;
288 if (process_info
->env_variables
) {
291 gunichar2
*str
, *ptr
;
295 for (i
= 0; i
< mono_array_length (process_info
->env_variables
); i
++) {
296 var
= mono_array_get (process_info
->env_variables
, MonoString
*, i
);
298 len
+= mono_string_length (var
) * sizeof (gunichar2
);
301 len
+= sizeof (gunichar2
);
303 /* null-terminated */
304 len
+= sizeof (gunichar2
);
306 env_vars
= ptr
= g_new0 (gunichar2
, len
);
308 for (i
= 0; i
< mono_array_length (process_info
->env_variables
); i
++) {
309 var
= mono_array_get (process_info
->env_variables
, MonoString
*, i
);
311 memcpy (ptr
, mono_string_chars (var
), mono_string_length (var
) * sizeof (gunichar2
));
312 ptr
+= mono_string_length (var
);
313 ptr
+= 1; // Skip over the null-separator
317 /* The default dir name is "". Turn that into NULL to mean
318 * "current directory"
320 if (proc_start_info
->working_directory
== NULL
|| mono_string_length (proc_start_info
->working_directory
) == 0)
323 dir
= mono_string_chars (proc_start_info
->working_directory
);
325 ret
= mono_process_create_process (process_info
, cmd
, creation_flags
, env_vars
, dir
, &startinfo
, &procinfo
);
330 process_info
->process_handle
= procinfo
.hProcess
;
331 /*process_info->thread_handle=procinfo.hThread;*/
332 process_info
->thread_handle
= NULL
;
333 if (procinfo
.hThread
!= NULL
&& procinfo
.hThread
!= INVALID_HANDLE_VALUE
)
334 CloseHandle (procinfo
.hThread
);
335 process_info
->pid
= procinfo
.dwProcessId
;
336 process_info
->tid
= procinfo
.dwThreadId
;
338 process_info
->pid
= -GetLastError ();
344 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
345 static inline gboolean
346 mono_process_win_enum_processes (DWORD
*pids
, DWORD count
, DWORD
*needed
)
348 return EnumProcesses (pids
, count
, needed
);
350 #endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */
353 ves_icall_System_Diagnostics_Process_GetProcesses_internal (void)
364 pids
= g_new0 (DWORD
, count
);
365 ret
= mono_process_win_enum_processes (pids
, count
* sizeof (guint32
), &needed
);
371 exc
= mono_get_exception_not_supported ("This system does not support EnumProcesses");
372 mono_set_pending_exception (exc
);
375 if (needed
< (count
* sizeof (guint32
)))
379 count
= (count
* 3) / 2;
382 count
= needed
/ sizeof (guint32
);
383 procs
= mono_array_new_checked (mono_domain_get (), mono_get_int32_class (), count
, &error
);
384 if (mono_error_set_pending_exception (&error
)) {
389 memcpy (mono_array_addr (procs
, guint32
, 0), pids
, needed
);
397 ves_icall_Microsoft_Win32_NativeMethods_CloseProcess (gpointer handle
)
399 return CloseHandle (handle
);
403 ves_icall_Microsoft_Win32_NativeMethods_TerminateProcess (gpointer handle
, gint32 exitcode
)
405 return TerminateProcess (handle
, exitcode
);
409 ves_icall_Microsoft_Win32_NativeMethods_GetExitCodeProcess (gpointer handle
, gint32
*exitcode
)
411 return GetExitCodeProcess (handle
, exitcode
);
414 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
415 static inline MonoBoolean
416 mono_icall_get_process_working_set_size (gpointer handle
, gsize
*min
, gsize
*max
)
418 return GetProcessWorkingSetSize (handle
, min
, max
);
420 #endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */
423 ves_icall_Microsoft_Win32_NativeMethods_GetProcessWorkingSetSize (gpointer handle
, gsize
*min
, gsize
*max
)
425 return mono_icall_get_process_working_set_size (handle
, min
, max
);
428 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
429 static inline MonoBoolean
430 mono_icall_set_process_working_set_size (gpointer handle
, gsize min
, gsize max
)
432 return SetProcessWorkingSetSize (handle
, min
, max
);
434 #endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */
437 ves_icall_Microsoft_Win32_NativeMethods_SetProcessWorkingSetSize (gpointer handle
, gsize min
, gsize max
)
439 return mono_icall_set_process_working_set_size (handle
, min
, max
);
442 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
444 mono_icall_get_priority_class (gpointer handle
)
446 return GetPriorityClass (handle
);
448 #endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */
451 ves_icall_Microsoft_Win32_NativeMethods_GetPriorityClass (gpointer handle
)
453 return mono_icall_get_priority_class (handle
);
456 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
457 static inline MonoBoolean
458 mono_icall_set_priority_class (gpointer handle
, gint32 priorityClass
)
460 return SetPriorityClass (handle
, (guint32
) priorityClass
);
462 #endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */
465 ves_icall_Microsoft_Win32_NativeMethods_SetPriorityClass (gpointer handle
, gint32 priorityClass
)
467 return mono_icall_set_priority_class (handle
, priorityClass
);
471 ves_icall_Microsoft_Win32_NativeMethods_GetProcessTimes (gpointer handle
, gint64
*creationtime
, gint64
*exittime
, gint64
*kerneltime
, gint64
*usertime
)
473 return GetProcessTimes (handle
, (LPFILETIME
) creationtime
, (LPFILETIME
) exittime
, (LPFILETIME
) kerneltime
, (LPFILETIME
) usertime
);
477 ves_icall_Microsoft_Win32_NativeMethods_GetCurrentProcess (void)
479 return GetCurrentProcess ();