[Mono.Runtime.Tests] Exclude simd tests
[mono-project.git] / mono / metadata / w32process-win32.c
blobfed51598fab44e79a1700fa8fc9b3d472802febe
1 /**
2 * \file
3 * System.Diagnostics.Process support
5 * Author:
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.
13 #include <config.h>
15 #include <glib.h>
16 #include <string.h>
18 #include <winsock2.h>
19 #include <windows.h>
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)
37 #include <shellapi.h>
38 #endif
39 #include "icall-decl.h"
41 void
42 mono_w32process_init (void)
46 void
47 mono_w32process_cleanup (void)
51 void
52 mono_w32process_signal_finished (void)
56 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
57 HANDLE
58 ves_icall_System_Diagnostics_Process_GetProcess_internal (guint32 pid)
60 HANDLE handle;
62 /* GetCurrentProcess returns a pseudo-handle, so use
63 * OpenProcess instead
65 handle = OpenProcess (PROCESS_ALL_ACCESS, TRUE, pid);
66 if (handle == NULL)
67 /* FIXME: Throw an exception */
68 return NULL;
69 return handle;
71 #endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT | HAVE_UWP_WINAPI_SUPPORT) */
73 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
74 MonoBoolean
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};
81 gboolean ret;
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;
91 if (coop.length.verb)
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);
99 else
100 shellex.fMask = (gulong)(shellex.fMask | SEE_MASK_FLAG_NO_UI);
102 ret = ShellExecuteEx (&shellex);
103 if (ret == FALSE) {
104 process_info->pid = -GetLastError ();
105 } else {
106 process_info->process_handle = shellex.hProcess;
107 #if !defined(MONO_CROSS_COMPILE)
108 process_info->pid = GetProcessId (shellex.hProcess);
109 #else
110 process_info->pid = 0;
111 #endif
114 mono_createprocess_coop_cleanup (&coop);
116 return ret;
118 #endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */
120 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
121 static inline void
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;
129 return;
132 static gboolean
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,
145 coop->domain,
146 mono_process_info->password,
147 logon_flags,
148 NULL,
149 cmd_chars,
150 creation_flags,
151 env_vars, dir, start_info, process_info);
152 } else {
153 result = CreateProcessW (NULL,
154 cmd_chars,
155 NULL,
156 NULL,
157 TRUE,
158 creation_flags,
159 env_vars,
160 dir,
161 start_info,
162 process_info);
165 mono_gchandle_free_internal (cmd_gchandle);
167 return result;
169 #endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */
171 static gchar*
172 process_unquote_application_name (gchar *appname)
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++;
182 return appname;
185 static gchar*
186 process_quote_path (const gchar *path)
188 gchar *res = g_shell_quote (path);
189 gchar *q = res;
190 while (*q) {
191 if (*q == '\'')
192 *q = '\"';
193 q++;
195 return res;
198 /* Only used when UseShellExecute is false */
199 static gboolean
200 process_complete_path (const gunichar2 *appname, gchar **completed)
202 // FIXME This function should stick to gunichar2.
204 char *utf8app;
205 char *utf8appmemory = NULL;
206 char *found = NULL;
207 gboolean result;
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);
214 result = TRUE;
215 goto exit;
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);
220 result = TRUE;
221 goto exit;
224 found = g_find_program_in_path (utf8app);
225 if (found == NULL) {
226 *completed = NULL;
227 result = FALSE;
228 goto exit;
231 *completed = process_quote_path (found);
232 exit:
233 g_free (found);
234 g_free (utf8appmemory);
235 return result;
238 static gboolean
239 process_get_shell_arguments (MonoCreateProcessCoop *proc_start_info, MonoStringHandle *cmd, MonoError *error)
241 char *spath = NULL;
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);
261 else {
262 *cmd = mono_string_new_utf8_len (mono_domain_get (), spath, strlen (spath), error);
263 goto_if_nok (error, error);
267 exit:
268 g_free (spath);
269 g_free (cmd_utf8);
270 g_free (new_cmd);
271 return !MONO_HANDLE_IS_NULL (*cmd);
272 error:
273 *cmd = NULL_HANDLE_STRING;
274 goto exit;
277 MonoBoolean
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);
284 gboolean ret;
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;
301 ret = FALSE;
302 goto exit;
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);
319 env_vars = ptr;
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);
339 g_free (env_vars);
341 if (ret) {
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;
347 } else {
348 // FIXME This should be passed back separately.
349 process_info->pid = -GetLastError ();
352 exit:
353 mono_createprocess_coop_cleanup (&coop);
354 return ret;
357 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
358 static gboolean
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) */
365 MonoArray *
366 ves_icall_System_Diagnostics_Process_GetProcesses_internal (void)
368 ERROR_DECL (error);
369 MonoArray *procs = NULL;
370 DWORD needed = 0;
371 DWORD *pids = NULL;
372 int count = 512;
374 do {
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);
380 goto exit;
382 if (needed < (count * sizeof (guint32)))
383 break;
384 g_free (pids);
385 pids = NULL;
386 count = (count * 3) / 2;
387 } while (TRUE);
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)) {
392 procs = NULL;
393 goto exit;
396 memcpy (mono_array_addr_internal (procs, guint32, 0), pids, needed);
397 exit:
398 g_free (pids);
399 return procs;
402 MonoBoolean
403 ves_icall_Microsoft_Win32_NativeMethods_CloseProcess (gpointer handle, MonoError *error)
405 return CloseHandle (handle);
408 MonoBoolean
409 ves_icall_Microsoft_Win32_NativeMethods_TerminateProcess (gpointer handle, gint32 exitcode, MonoError *error)
411 return TerminateProcess (handle, exitcode);
414 MonoBoolean
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) */
428 MonoBoolean
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) */
442 MonoBoolean
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)
449 static inline gint32
450 mono_icall_get_priority_class (gpointer handle)
452 return GetPriorityClass (handle);
454 #endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */
456 gint32
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) */
470 MonoBoolean
471 ves_icall_Microsoft_Win32_NativeMethods_SetPriorityClass (gpointer handle, gint32 priorityClass, MonoError *error)
473 return mono_icall_set_priority_class (handle, priorityClass);
476 MonoBoolean
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);
482 gpointer
483 ves_icall_Microsoft_Win32_NativeMethods_GetCurrentProcess (MonoError *error)
485 return GetCurrentProcess ();