3 * System.Diagnostics.Process support
6 * Dick Porter (dick@ximian.com)
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.
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 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
39 #include "icall-decl.h"
42 mono_w32process_init (void)
47 mono_w32process_cleanup (void)
52 mono_w32process_signal_finished (void)
56 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
58 ves_icall_System_Diagnostics_Process_GetProcess_internal (guint32 pid
)
62 /* GetCurrentProcess returns a pseudo-handle, so use
65 handle
= OpenProcess (PROCESS_ALL_ACCESS
, TRUE
, pid
);
67 /* FIXME: Throw an exception */
71 #endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT) */
73 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
75 ves_icall_System_Diagnostics_Process_ShellExecuteEx_internal (MonoW32ProcessStartInfoHandle proc_start_info
, MonoW32ProcessInfo
*process_info
, MonoError
*error
)
77 MonoCreateProcessCoop coop
;
78 mono_createprocess_coop_init (&coop
, proc_start_info
, process_info
);
80 SHELLEXECUTEINFO shellex
= {0};
83 shellex
.cbSize
= sizeof(SHELLEXECUTEINFO
);
84 shellex
.fMask
= (gulong
)(SEE_MASK_FLAG_DDEWAIT
| SEE_MASK_NOCLOSEPROCESS
| SEE_MASK_UNICODE
);
85 shellex
.nShow
= (gulong
)MONO_HANDLE_GETVAL (proc_start_info
, window_style
);
86 shellex
.nShow
= (gulong
)((shellex
.nShow
== 0) ? 1 : (shellex
.nShow
== 1 ? 0 : shellex
.nShow
));
88 shellex
.lpFile
= coop
.filename
;
89 shellex
.lpParameters
= coop
.arguments
;
92 shellex
.lpVerb
= coop
.verb
;
94 if (coop
.length
.working_directory
)
95 shellex
.lpDirectory
= coop
.working_directory
;
97 if (MONO_HANDLE_GETVAL (proc_start_info
, error_dialog
))
98 shellex
.hwnd
= (HWND
)MONO_HANDLE_GETVAL (proc_start_info
, error_dialog_parent_handle
);
100 shellex
.fMask
= (gulong
)(shellex
.fMask
| SEE_MASK_FLAG_NO_UI
);
102 ret
= ShellExecuteEx (&shellex
);
104 process_info
->pid
= -GetLastError ();
106 process_info
->process_handle
= shellex
.hProcess
;
107 #if !defined(MONO_CROSS_COMPILE)
108 process_info
->pid
= GetProcessId (shellex
.hProcess
);
110 process_info
->pid
= 0;
114 mono_createprocess_coop_cleanup (&coop
);
118 #endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */
120 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
122 mono_process_init_startup_info (HANDLE stdin_handle
, HANDLE stdout_handle
, HANDLE stderr_handle
, STARTUPINFO
*startinfo
)
124 startinfo
->cb
= sizeof(STARTUPINFO
);
125 startinfo
->dwFlags
= STARTF_USESTDHANDLES
;
126 startinfo
->hStdInput
= stdin_handle
;
127 startinfo
->hStdOutput
= stdout_handle
;
128 startinfo
->hStdError
= stderr_handle
;
133 mono_process_create_process (MonoCreateProcessCoop
*coop
, MonoW32ProcessInfo
*mono_process_info
,
134 MonoStringHandle cmd
, guint32 creation_flags
, gunichar2
*env_vars
, gunichar2
*dir
, STARTUPINFO
*start_info
,
135 PROCESS_INFORMATION
*process_info
)
137 gboolean result
= FALSE
;
138 gchandle_t cmd_gchandle
= 0;
139 gunichar2
*cmd_chars
= MONO_HANDLE_IS_NULL (cmd
) ? NULL
: mono_string_handle_pin_chars (cmd
, &cmd_gchandle
);
141 if (coop
->username
) {
142 guint32 logon_flags
= mono_process_info
->load_user_profile
? LOGON_WITH_PROFILE
: 0;
144 result
= CreateProcessWithLogonW (coop
->username
,
146 mono_process_info
->password
,
151 env_vars
, dir
, start_info
, process_info
);
153 result
= CreateProcessW (NULL
,
165 mono_gchandle_free_internal (cmd_gchandle
);
169 #endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */
172 process_unquote_application_name (gchar
*appname
)
174 size_t len
= strlen (appname
);
176 if (appname
[len
-1] == '\"')
177 appname
[len
-1] = '\0';
178 if (appname
[0] == '\"')
186 process_quote_path (const gchar
*path
)
188 gchar
*res
= g_shell_quote (path
);
198 /* Only used when UseShellExecute is false */
200 process_complete_path (const gunichar2
*appname
, gchar
**completed
)
202 // FIXME This function should stick to gunichar2.
205 char *utf8appmemory
= NULL
;
209 utf8appmemory
= g_utf16_to_utf8 (appname
, -1, NULL
, NULL
, NULL
);
210 utf8app
= process_unquote_application_name (utf8appmemory
);
212 if (g_path_is_absolute (utf8app
)) {
213 *completed
= process_quote_path (utf8app
);
218 if (g_file_test (utf8app
, G_FILE_TEST_IS_EXECUTABLE
) && !g_file_test (utf8app
, G_FILE_TEST_IS_DIR
)) {
219 *completed
= process_quote_path (utf8app
);
224 found
= g_find_program_in_path (utf8app
);
231 *completed
= process_quote_path (found
);
234 g_free (utf8appmemory
);
239 process_get_shell_arguments (MonoCreateProcessCoop
*proc_start_info
, MonoStringHandle
*cmd
, MonoError
*error
)
242 char *new_cmd
= NULL
;
243 char *cmd_utf8
= NULL
;
245 *cmd
= proc_start_info
->coophandle
.arguments
;
247 // FIXME There are excess utf8 <=> gunichar2 conversions here.
248 // We are either returning spath, or spath + " " + cmd.
249 // Just use gunichar2. Maybe move logic to C#.
251 if (process_complete_path (proc_start_info
->filename
, &spath
)) {
252 /* Seems like our CreateProcess does not work as the windows one.
253 * This hack is needed to deal with paths containing spaces */
254 if (!MONO_HANDLE_IS_NULL (*cmd
)) {
255 cmd_utf8
= mono_string_handle_to_utf8 (*cmd
, error
);
256 goto_if_nok (error
, error
);
257 new_cmd
= g_strdup_printf ("%s %s", spath
, cmd_utf8
);
258 *cmd
= mono_string_new_utf8_len (mono_domain_get (), new_cmd
, strlen (new_cmd
), error
);
259 goto_if_nok (error
, error
);
262 *cmd
= mono_string_new_utf8_len (mono_domain_get (), spath
, strlen (spath
), error
);
263 goto_if_nok (error
, error
);
271 return !MONO_HANDLE_IS_NULL (*cmd
);
273 *cmd
= NULL_HANDLE_STRING
;
278 ves_icall_System_Diagnostics_Process_CreateProcess_internal (MonoW32ProcessStartInfoHandle proc_start_info
,
279 HANDLE stdin_handle
, HANDLE stdout_handle
, HANDLE stderr_handle
, MonoW32ProcessInfo
*process_info
, MonoError
*error
)
281 MonoCreateProcessCoop coop
;
282 mono_createprocess_coop_init (&coop
, proc_start_info
, process_info
);
285 gunichar2
*dir
= NULL
;
286 STARTUPINFO startinfo
={0};
287 PROCESS_INFORMATION procinfo
;
288 gunichar2
*env_vars
= NULL
;
289 MonoStringHandle cmd
= NULL_HANDLE_STRING
;
290 guint32 creation_flags
;
292 mono_process_init_startup_info (stdin_handle
, stdout_handle
, stderr_handle
, &startinfo
);
294 creation_flags
= CREATE_UNICODE_ENVIRONMENT
;
295 if (MONO_HANDLE_GETVAL (proc_start_info
, create_no_window
))
296 creation_flags
|= CREATE_NO_WINDOW
;
298 if (process_get_shell_arguments (&coop
, &cmd
, error
) == FALSE
) {
299 // FIXME This should be passed back separately.
300 process_info
->pid
= -ERROR_FILE_NOT_FOUND
;
305 if (process_info
->env_variables
) {
306 MonoArrayHandle array
= MONO_HANDLE_NEW (MonoArray
, process_info
->env_variables
);
307 MonoStringHandle var
= MONO_HANDLE_NEW (MonoString
, NULL
);
308 gsize
const array_length
= mono_array_handle_length (array
);
310 // nul-separated and nul-terminated
311 gsize len
= array_length
+ 1 + !array_length
;
313 for (gsize i
= 0; i
< array_length
; i
++) {
314 MONO_HANDLE_ARRAY_GETREF (var
, array
, i
);
315 len
+= mono_string_handle_length (var
);
318 gunichar2
*ptr
= g_new0 (gunichar2
, len
);
321 for (gsize i
= 0; i
< array_length
; i
++) {
322 MONO_HANDLE_ARRAY_GETREF (var
, array
, i
);
323 gchandle_t gchandle
= 0;
324 memcpy (ptr
, mono_string_handle_pin_chars (var
, &gchandle
), mono_string_handle_length (var
) * sizeof (gunichar2
));
325 mono_gchandle_free_internal (gchandle
);
326 ptr
+= mono_string_handle_length (var
);
327 ptr
+= 1; // Skip over the null-separator
331 /* The default dir name is "". Turn that into NULL to mean
332 * "current directory"
334 if (coop
.length
.working_directory
)
335 dir
= coop
.working_directory
;
337 ret
= mono_process_create_process (&coop
, process_info
, cmd
, creation_flags
, env_vars
, dir
, &startinfo
, &procinfo
);
342 process_info
->process_handle
= procinfo
.hProcess
;
343 /*process_info->thread_handle=procinfo.hThread;*/
344 if (procinfo
.hThread
!= NULL
&& procinfo
.hThread
!= INVALID_HANDLE_VALUE
)
345 CloseHandle (procinfo
.hThread
);
346 process_info
->pid
= procinfo
.dwProcessId
;
348 // FIXME This should be passed back separately.
349 process_info
->pid
= -GetLastError ();
353 mono_createprocess_coop_cleanup (&coop
);
357 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
359 mono_process_win_enum_processes (DWORD
*pids
, DWORD count
, DWORD
*needed
)
361 return !!EnumProcesses (pids
, count
, needed
);
363 #endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */
366 ves_icall_System_Diagnostics_Process_GetProcesses_internal (void)
369 MonoArray
*procs
= NULL
;
375 pids
= g_new0 (DWORD
, count
);
376 if (!mono_process_win_enum_processes (pids
, count
* sizeof (guint32
), &needed
)) {
377 // FIXME GetLastError?
378 mono_error_set_not_supported (error
, "This system does not support EnumProcesses");
379 mono_error_set_pending_exception (error
);
382 if (needed
< (count
* sizeof (guint32
)))
386 count
= (count
* 3) / 2;
389 count
= needed
/ sizeof (guint32
);
390 procs
= mono_array_new_checked (mono_domain_get (), mono_get_int32_class (), count
, error
);
391 if (mono_error_set_pending_exception (error
)) {
396 memcpy (mono_array_addr_internal (procs
, guint32
, 0), pids
, needed
);
403 ves_icall_Microsoft_Win32_NativeMethods_CloseProcess (gpointer handle
, MonoError
*error
)
405 return CloseHandle (handle
);
409 ves_icall_Microsoft_Win32_NativeMethods_TerminateProcess (gpointer handle
, gint32 exitcode
, MonoError
*error
)
411 return TerminateProcess (handle
, exitcode
);
415 ves_icall_Microsoft_Win32_NativeMethods_GetExitCodeProcess (gpointer handle
, gint32
*exitcode
, MonoError
*error
)
417 return GetExitCodeProcess (handle
, (PDWORD
)exitcode
);
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
)
424 return GetProcessWorkingSetSize (handle
, (PSIZE_T
)min
, (PSIZE_T
)max
);
426 #endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */
429 ves_icall_Microsoft_Win32_NativeMethods_GetProcessWorkingSetSize (gpointer handle
, gsize
*min
, gsize
*max
, MonoError
*error
)
431 return mono_icall_get_process_working_set_size (handle
, min
, max
);
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
)
438 return SetProcessWorkingSetSize (handle
, min
, max
);
440 #endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */
443 ves_icall_Microsoft_Win32_NativeMethods_SetProcessWorkingSetSize (gpointer handle
, gsize min
, gsize max
, MonoError
*error
)
445 return mono_icall_set_process_working_set_size (handle
, min
, max
);
448 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
450 mono_icall_get_priority_class (gpointer handle
)
452 return GetPriorityClass (handle
);
454 #endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */
457 ves_icall_Microsoft_Win32_NativeMethods_GetPriorityClass (gpointer handle
, MonoError
*error
)
459 return mono_icall_get_priority_class (handle
);
462 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
463 static inline MonoBoolean
464 mono_icall_set_priority_class (gpointer handle
, gint32 priorityClass
)
466 return SetPriorityClass (handle
, (guint32
) priorityClass
);
468 #endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */
471 ves_icall_Microsoft_Win32_NativeMethods_SetPriorityClass (gpointer handle
, gint32 priorityClass
, MonoError
*error
)
473 return mono_icall_set_priority_class (handle
, priorityClass
);
477 ves_icall_Microsoft_Win32_NativeMethods_GetProcessTimes (gpointer handle
, gint64
*creationtime
, gint64
*exittime
, gint64
*kerneltime
, gint64
*usertime
, MonoError
*error
)
479 return GetProcessTimes (handle
, (LPFILETIME
) creationtime
, (LPFILETIME
) exittime
, (LPFILETIME
) kerneltime
, (LPFILETIME
) usertime
);
483 ves_icall_Microsoft_Win32_NativeMethods_GetCurrentProcess (MonoError
*error
)
485 return GetCurrentProcess ();