2 * process.c: System.Diagnostics.Process support
5 * Dick Porter (dick@ximian.com)
7 * Copyright 2002 Ximian, Inc.
8 * Copyright 2002-2006 Novell, Inc.
16 #include <mono/metadata/object.h>
17 #include <mono/metadata/process.h>
18 #include <mono/metadata/assembly.h>
19 #include <mono/metadata/appdomain.h>
20 #include <mono/metadata/image.h>
21 #include <mono/metadata/cil-coff.h>
22 #include <mono/metadata/exception.h>
23 #include <mono/utils/strenc.h>
24 #include <mono/utils/mono-proclib.h>
25 #include <mono/io-layer/io-layer.h>
26 /* FIXME: fix this code to not depend so much on the inetrnals */
27 #include <mono/metadata/class-internals.h>
30 /* define LOGDEBUG(...) g_message(__VA_ARGS__) */
33 HANDLE
ves_icall_System_Diagnostics_Process_GetProcess_internal (guint32 pid
)
39 /* GetCurrentProcess returns a pseudo-handle, so use
42 handle
=OpenProcess (PROCESS_ALL_ACCESS
, TRUE
, pid
);
45 /* FIXME: Throw an exception */
52 guint32
ves_icall_System_Diagnostics_Process_GetPid_internal (void)
56 return(GetCurrentProcessId ());
59 void ves_icall_System_Diagnostics_Process_Process_free_internal (MonoObject
*this,
65 g_message ("%s: Closing process %p, handle %p", __func__
, this, process
);
68 CloseHandle (process
);
71 #define STASH_SYS_ASS(this) \
72 if(system_assembly == NULL) { \
73 system_assembly=this->vtable->klass->image; \
76 static MonoImage
*system_assembly
=NULL
;
78 static guint32
unicode_chars (const gunichar2
*str
)
90 static void process_set_field_object (MonoObject
*obj
, const gchar
*fieldname
,
93 MonoClassField
*field
;
95 LOGDEBUG (g_message ("%s: Setting field %s to object at %p", __func__
, fieldname
, data
));
97 field
=mono_class_get_field_from_name (mono_object_class (obj
),
99 mono_gc_wbarrier_generic_store (((char *)obj
) + field
->offset
, data
);
102 static void process_set_field_string (MonoObject
*obj
, const gchar
*fieldname
,
103 const gunichar2
*val
, guint32 len
)
105 MonoClassField
*field
;
108 LOGDEBUG (g_message ("%s: Setting field %s to [%s]", __func__
, fieldname
, g_utf16_to_utf8 (val
, len
, NULL
, NULL
, NULL
)));
110 string
=mono_string_new_utf16 (mono_object_domain (obj
), val
, len
);
112 field
=mono_class_get_field_from_name (mono_object_class (obj
),
114 mono_gc_wbarrier_generic_store (((char *)obj
) + field
->offset
, (MonoObject
*)string
);
117 static void process_set_field_int (MonoObject
*obj
, const gchar
*fieldname
,
120 MonoClassField
*field
;
122 LOGDEBUG (g_message ("%s: Setting field %s to %d", __func__
,fieldname
, val
));
124 field
=mono_class_get_field_from_name (mono_object_class (obj
),
126 *(guint32
*)(((char *)obj
) + field
->offset
)=val
;
129 static void process_set_field_intptr (MonoObject
*obj
, const gchar
*fieldname
,
132 MonoClassField
*field
;
134 LOGDEBUG (g_message ("%s: Setting field %s to %p", __func__
, fieldname
, val
));
136 field
=mono_class_get_field_from_name (mono_object_class (obj
),
138 *(gpointer
*)(((char *)obj
) + field
->offset
)=val
;
141 static void process_set_field_bool (MonoObject
*obj
, const gchar
*fieldname
,
144 MonoClassField
*field
;
146 LOGDEBUG (g_message ("%s: Setting field %s to %s", __func__
, fieldname
, val
?"TRUE":"FALSE"));
148 field
=mono_class_get_field_from_name (mono_object_class (obj
),
150 *(guint8
*)(((char *)obj
) + field
->offset
)=val
;
153 #define SFI_COMMENTS "\\StringFileInfo\\%02X%02X%02X%02X\\Comments"
154 #define SFI_COMPANYNAME "\\StringFileInfo\\%02X%02X%02X%02X\\CompanyName"
155 #define SFI_FILEDESCRIPTION "\\StringFileInfo\\%02X%02X%02X%02X\\FileDescription"
156 #define SFI_FILEVERSION "\\StringFileInfo\\%02X%02X%02X%02X\\FileVersion"
157 #define SFI_INTERNALNAME "\\StringFileInfo\\%02X%02X%02X%02X\\InternalName"
158 #define SFI_LEGALCOPYRIGHT "\\StringFileInfo\\%02X%02X%02X%02X\\LegalCopyright"
159 #define SFI_LEGALTRADEMARKS "\\StringFileInfo\\%02X%02X%02X%02X\\LegalTrademarks"
160 #define SFI_ORIGINALFILENAME "\\StringFileInfo\\%02X%02X%02X%02X\\OriginalFilename"
161 #define SFI_PRIVATEBUILD "\\StringFileInfo\\%02X%02X%02X%02X\\PrivateBuild"
162 #define SFI_PRODUCTNAME "\\StringFileInfo\\%02X%02X%02X%02X\\ProductName"
163 #define SFI_PRODUCTVERSION "\\StringFileInfo\\%02X%02X%02X%02X\\ProductVersion"
164 #define SFI_SPECIALBUILD "\\StringFileInfo\\%02X%02X%02X%02X\\SpecialBuild"
165 #define EMPTY_STRING (gunichar2*)"\000\000"
167 static void process_module_string_read (MonoObject
*filever
, gpointer data
,
168 const gchar
*fieldname
,
169 guchar lang_hi
, guchar lang_lo
,
172 gchar
*lang_key_utf8
;
173 gunichar2
*lang_key
, *buffer
;
176 lang_key_utf8
= g_strdup_printf (key
, lang_lo
, lang_hi
, 0x04, 0xb0);
178 LOGDEBUG (g_message ("%s: asking for [%s]", __func__
, lang_key_utf8
));
180 lang_key
= g_utf8_to_utf16 (lang_key_utf8
, -1, NULL
, NULL
, NULL
);
182 if (VerQueryValue (data
, lang_key
, (gpointer
*)&buffer
, &chars
) && chars
> 0) {
183 LOGDEBUG (g_message ("%s: found %d chars of [%s]", __func__
, chars
, g_utf16_to_utf8 (buffer
, chars
, NULL
, NULL
, NULL
)));
184 /* chars includes trailing null */
185 process_set_field_string (filever
, fieldname
, buffer
, chars
- 1);
187 process_set_field_string (filever
, fieldname
, EMPTY_STRING
, 0);
191 g_free (lang_key_utf8
);
194 static void process_module_stringtable (MonoObject
*filever
, gpointer data
,
195 guchar lang_hi
, guchar lang_lo
)
197 process_module_string_read (filever
, data
, "comments", lang_hi
, lang_lo
,
199 process_module_string_read (filever
, data
, "companyname", lang_hi
,
200 lang_lo
, SFI_COMPANYNAME
);
201 process_module_string_read (filever
, data
, "filedescription", lang_hi
,
202 lang_lo
, SFI_FILEDESCRIPTION
);
203 process_module_string_read (filever
, data
, "fileversion", lang_hi
,
204 lang_lo
, SFI_FILEVERSION
);
205 process_module_string_read (filever
, data
, "internalname", lang_hi
,
206 lang_lo
, SFI_INTERNALNAME
);
207 process_module_string_read (filever
, data
, "legalcopyright", lang_hi
,
208 lang_lo
, SFI_LEGALCOPYRIGHT
);
209 process_module_string_read (filever
, data
, "legaltrademarks", lang_hi
,
210 lang_lo
, SFI_LEGALTRADEMARKS
);
211 process_module_string_read (filever
, data
, "originalfilename", lang_hi
,
212 lang_lo
, SFI_ORIGINALFILENAME
);
213 process_module_string_read (filever
, data
, "privatebuild", lang_hi
,
214 lang_lo
, SFI_PRIVATEBUILD
);
215 process_module_string_read (filever
, data
, "productname", lang_hi
,
216 lang_lo
, SFI_PRODUCTNAME
);
217 process_module_string_read (filever
, data
, "productversion", lang_hi
,
218 lang_lo
, SFI_PRODUCTVERSION
);
219 process_module_string_read (filever
, data
, "specialbuild", lang_hi
,
220 lang_lo
, SFI_SPECIALBUILD
);
223 static void process_get_fileversion (MonoObject
*filever
, gunichar2
*filename
)
226 VS_FIXEDFILEINFO
*ffi
;
231 UINT ffi_size
, trans_size
;
233 gunichar2 lang_buf
[128];
234 guint32 lang
, lang_count
;
236 datalen
= GetFileVersionInfoSize (filename
, &verinfohandle
);
238 data
= g_malloc0 (datalen
);
239 ok
= GetFileVersionInfo (filename
, verinfohandle
, datalen
,
242 query
= g_utf8_to_utf16 ("\\", -1, NULL
, NULL
, NULL
);
248 if (VerQueryValue (data
, query
, (gpointer
*)&ffi
,
250 LOGDEBUG (g_message ("%s: recording assembly: FileName [%s] FileVersionInfo [%d.%d.%d.%d]", __func__
, g_utf16_to_utf8 (filename
, -1, NULL
, NULL
, NULL
), HIWORD (ffi
->dwFileVersionMS
), LOWORD (ffi
->dwFileVersionMS
), HIWORD (ffi
->dwFileVersionLS
), LOWORD (ffi
->dwFileVersionLS
)));
252 process_set_field_int (filever
, "filemajorpart", HIWORD (ffi
->dwFileVersionMS
));
253 process_set_field_int (filever
, "fileminorpart", LOWORD (ffi
->dwFileVersionMS
));
254 process_set_field_int (filever
, "filebuildpart", HIWORD (ffi
->dwFileVersionLS
));
255 process_set_field_int (filever
, "fileprivatepart", LOWORD (ffi
->dwFileVersionLS
));
257 process_set_field_int (filever
, "productmajorpart", HIWORD (ffi
->dwProductVersionMS
));
258 process_set_field_int (filever
, "productminorpart", LOWORD (ffi
->dwProductVersionMS
));
259 process_set_field_int (filever
, "productbuildpart", HIWORD (ffi
->dwProductVersionLS
));
260 process_set_field_int (filever
, "productprivatepart", LOWORD (ffi
->dwProductVersionLS
));
262 process_set_field_bool (filever
, "isdebug", (ffi
->dwFileFlags
& ffi
->dwFileFlagsMask
) & VS_FF_DEBUG
);
263 process_set_field_bool (filever
, "isprerelease", (ffi
->dwFileFlags
& ffi
->dwFileFlagsMask
) & VS_FF_PRERELEASE
);
264 process_set_field_bool (filever
, "ispatched", (ffi
->dwFileFlags
& ffi
->dwFileFlagsMask
) & VS_FF_PATCHED
);
265 process_set_field_bool (filever
, "isprivatebuild", (ffi
->dwFileFlags
& ffi
->dwFileFlagsMask
) & VS_FF_PRIVATEBUILD
);
266 process_set_field_bool (filever
, "isspecialbuild", (ffi
->dwFileFlags
& ffi
->dwFileFlagsMask
) & VS_FF_SPECIALBUILD
);
270 query
= g_utf8_to_utf16 ("\\VarFileInfo\\Translation", -1, NULL
, NULL
, NULL
);
276 if (VerQueryValue (data
, query
,
277 (gpointer
*)&trans_data
,
279 /* use the first language ID we see
281 if (trans_size
>= 4) {
282 LOGDEBUG (g_message("%s: %s has 0x%0x 0x%0x 0x%0x 0x%0x", __func__
, g_utf16_to_utf8 (filename
, -1, NULL
, NULL
, NULL
), trans_data
[0], trans_data
[1], trans_data
[2], trans_data
[3]));
283 lang
= (trans_data
[0]) |
284 (trans_data
[1] << 8) |
285 (trans_data
[2] << 16) |
286 (trans_data
[3] << 24);
287 /* Only give the lower 16 bits
288 * to VerLanguageName, as
289 * Windows gets confused
292 lang_count
= VerLanguageName (lang
& 0xFFFF, lang_buf
, 128);
294 process_set_field_string (filever
, "language", lang_buf
, lang_count
);
296 process_module_stringtable (filever
, data
, trans_data
[0], trans_data
[1]);
299 /* No strings, so set every field to
302 process_set_field_string (filever
,
305 process_set_field_string (filever
,
308 process_set_field_string (filever
,
311 process_set_field_string (filever
,
314 process_set_field_string (filever
,
317 process_set_field_string (filever
,
320 process_set_field_string (filever
,
323 process_set_field_string (filever
,
326 process_set_field_string (filever
,
329 process_set_field_string (filever
,
332 process_set_field_string (filever
,
335 process_set_field_string (filever
,
339 /* And language seems to be set to
340 * en_US according to bug 374600
342 lang_count
= VerLanguageName (0x0409, lang_buf
, 128);
344 process_set_field_string (filever
, "language", lang_buf
, lang_count
);
354 static MonoObject
* process_add_module (HANDLE process
, HMODULE mod
, gunichar2
*filename
, gunichar2
*modulename
)
356 MonoClass
*proc_class
, *filever_class
;
357 MonoObject
*item
, *filever
;
358 MonoDomain
*domain
=mono_domain_get ();
362 /* Build a System.Diagnostics.ProcessModule with the data.
364 proc_class
=mono_class_from_name (system_assembly
, "System.Diagnostics",
366 item
=mono_object_new (domain
, proc_class
);
368 filever_class
=mono_class_from_name (system_assembly
,
369 "System.Diagnostics",
371 filever
=mono_object_new (domain
, filever_class
);
373 process_get_fileversion (filever
, filename
);
375 process_set_field_string (filever
, "filename", filename
,
376 unicode_chars (filename
));
378 ok
= GetModuleInformation (process
, mod
, &modinfo
, sizeof(MODULEINFO
));
380 process_set_field_intptr (item
, "baseaddr",
381 modinfo
.lpBaseOfDll
);
382 process_set_field_intptr (item
, "entryaddr",
384 process_set_field_int (item
, "memory_size",
385 modinfo
.SizeOfImage
);
387 process_set_field_string (item
, "filename", filename
,
388 unicode_chars (filename
));
389 process_set_field_string (item
, "modulename", modulename
,
390 unicode_chars (modulename
));
391 process_set_field_object (item
, "version_info", filever
);
396 /* Returns an array of System.Diagnostics.ProcessModule */
397 MonoArray
*ves_icall_System_Diagnostics_Process_GetModules_internal (MonoObject
*this, HANDLE process
)
399 MonoArray
*temp_arr
= NULL
;
402 gunichar2 filename
[MAX_PATH
];
403 gunichar2 modname
[MAX_PATH
];
406 guint32 i
, num_added
= 0;
408 STASH_SYS_ASS (this);
410 if (EnumProcessModules (process
, mods
, sizeof(mods
), &needed
)) {
411 count
= needed
/ sizeof(HMODULE
);
412 temp_arr
= mono_array_new (mono_domain_get (), mono_get_object_class (), count
);
413 for (i
= 0; i
< count
; i
++) {
414 if (GetModuleBaseName (process
, mods
[i
], modname
, MAX_PATH
) &&
415 GetModuleFileNameEx (process
, mods
[i
], filename
, MAX_PATH
)) {
416 MonoObject
*module
= process_add_module (process
, mods
[i
],
418 mono_array_setref (temp_arr
, num_added
++, module
);
423 if (count
== num_added
) {
426 /* shorter version of the array */
427 arr
= mono_array_new (mono_domain_get (), mono_get_object_class (), num_added
);
429 for (i
= 0; i
< num_added
; i
++)
430 mono_array_setref (arr
, i
, mono_array_get (temp_arr
, MonoObject
*, i
));
436 void ves_icall_System_Diagnostics_FileVersionInfo_GetVersionInfo_internal (MonoObject
*this, MonoString
*filename
)
440 STASH_SYS_ASS (this);
442 process_get_fileversion (this, mono_string_chars (filename
));
443 process_set_field_string (this, "filename",
444 mono_string_chars (filename
),
445 mono_string_length (filename
));
448 /* Only used when UseShellExecute is false */
450 quote_path (const gchar
*path
)
452 gchar
*res
= g_shell_quote (path
);
466 /* Only used when UseShellExecute is false */
468 complete_path (const gunichar2
*appname
, gchar
**completed
)
470 gchar
*utf8app
, *utf8appmemory
;
473 utf8appmemory
= utf8app
= g_utf16_to_utf8 (appname
, -1, NULL
, NULL
, NULL
);
474 #ifdef TARGET_WIN32 // Should this happen on all platforms?
476 // remove the quotes around utf8app.
478 len
= strlen (utf8app
);
480 if (utf8app
[len
-1] == '\"')
481 utf8app
[len
-1] = '\0';
482 if (utf8app
[0] == '\"')
488 if (g_path_is_absolute (utf8app
)) {
489 *completed
= quote_path (utf8app
);
490 g_free (utf8appmemory
);
494 if (g_file_test (utf8app
, G_FILE_TEST_IS_EXECUTABLE
) && !g_file_test (utf8app
, G_FILE_TEST_IS_DIR
)) {
495 *completed
= quote_path (utf8app
);
496 g_free (utf8appmemory
);
500 found
= g_find_program_in_path (utf8app
);
503 g_free (utf8appmemory
);
507 *completed
= quote_path (found
);
509 g_free (utf8appmemory
);
513 #ifndef HAVE_GETPROCESSID
514 /* Run-time GetProcessId detection for Windows */
516 #define HAVE_GETPROCESSID
518 typedef DWORD (WINAPI
*GETPROCESSID_PROC
) (HANDLE
);
519 typedef DWORD (WINAPI
*NTQUERYINFORMATIONPROCESS_PROC
) (HANDLE
, PROCESSINFOCLASS
, PVOID
, ULONG
, PULONG
);
520 typedef DWORD (WINAPI
*RTLNTSTATUSTODOSERROR_PROC
) (NTSTATUS
);
522 static DWORD WINAPI
GetProcessId_detect (HANDLE process
);
524 static GETPROCESSID_PROC GetProcessId
= &GetProcessId_detect
;
525 static NTQUERYINFORMATIONPROCESS_PROC NtQueryInformationProcess_proc
= NULL
;
526 static RTLNTSTATUSTODOSERROR_PROC RtlNtStatusToDosError_proc
= NULL
;
528 static DWORD WINAPI
GetProcessId_ntdll (HANDLE process
)
530 PROCESS_BASIC_INFORMATION pi
;
533 status
= NtQueryInformationProcess_proc (process
, ProcessBasicInformation
, &pi
, sizeof (pi
), NULL
);
534 if (NT_SUCCESS (status
)) {
535 return pi
.UniqueProcessId
;
537 SetLastError (RtlNtStatusToDosError_proc (status
));
542 static DWORD WINAPI
GetProcessId_stub (HANDLE process
)
544 SetLastError (ERROR_CALL_NOT_IMPLEMENTED
);
548 static DWORD WINAPI
GetProcessId_detect (HANDLE process
)
550 HMODULE module_handle
;
551 GETPROCESSID_PROC GetProcessId_kernel
;
553 /* Windows XP SP1 and above have GetProcessId API */
554 module_handle
= GetModuleHandle (L
"kernel32.dll");
555 if (module_handle
!= NULL
) {
556 GetProcessId_kernel
= (GETPROCESSID_PROC
) GetProcAddress (module_handle
, "GetProcessId");
557 if (GetProcessId_kernel
!= NULL
) {
558 GetProcessId
= GetProcessId_kernel
;
559 return GetProcessId (process
);
563 /* Windows 2000 and above have deprecated NtQueryInformationProcess API */
564 module_handle
= GetModuleHandle (L
"ntdll.dll");
565 if (module_handle
!= NULL
) {
566 NtQueryInformationProcess_proc
= (NTQUERYINFORMATIONPROCESS_PROC
) GetProcAddress (module_handle
, "NtQueryInformationProcess");
567 if (NtQueryInformationProcess_proc
!= NULL
) {
568 RtlNtStatusToDosError_proc
= (RTLNTSTATUSTODOSERROR_PROC
) GetProcAddress (module_handle
, "RtlNtStatusToDosError");
569 if (RtlNtStatusToDosError_proc
!= NULL
) {
570 GetProcessId
= &GetProcessId_ntdll
;
571 return GetProcessId (process
);
576 /* Fall back to ERROR_CALL_NOT_IMPLEMENTED */
577 GetProcessId
= &GetProcessId_stub
;
578 return GetProcessId (process
);
580 #endif /* HOST_WIN32 */
581 #endif /* !HAVE_GETPROCESSID */
583 MonoBoolean
ves_icall_System_Diagnostics_Process_ShellExecuteEx_internal (MonoProcessStartInfo
*proc_start_info
, MonoProcInfo
*process_info
)
585 SHELLEXECUTEINFO shellex
= {0};
588 shellex
.cbSize
= sizeof(SHELLEXECUTEINFO
);
589 shellex
.fMask
= SEE_MASK_FLAG_DDEWAIT
| SEE_MASK_NOCLOSEPROCESS
| SEE_MASK_UNICODE
;
590 shellex
.nShow
= proc_start_info
->window_style
;
591 shellex
.nShow
= (shellex
.nShow
== 0) ? 1 : (shellex
.nShow
== 1 ? 0 : shellex
.nShow
);
594 if (proc_start_info
->filename
!= NULL
) {
595 shellex
.lpFile
= mono_string_chars (proc_start_info
->filename
);
598 if (proc_start_info
->arguments
!= NULL
) {
599 shellex
.lpParameters
= mono_string_chars (proc_start_info
->arguments
);
602 if (proc_start_info
->verb
!= NULL
&&
603 mono_string_length (proc_start_info
->verb
) != 0) {
604 shellex
.lpVerb
= mono_string_chars (proc_start_info
->verb
);
607 if (proc_start_info
->working_directory
!= NULL
&&
608 mono_string_length (proc_start_info
->working_directory
) != 0) {
609 shellex
.lpDirectory
= mono_string_chars (proc_start_info
->working_directory
);
612 if (proc_start_info
->error_dialog
) {
613 shellex
.hwnd
= proc_start_info
->error_dialog_parent_handle
;
615 shellex
.fMask
|= SEE_MASK_FLAG_NO_UI
;
618 ret
= ShellExecuteEx (&shellex
);
620 process_info
->pid
= -GetLastError ();
622 process_info
->process_handle
= shellex
.hProcess
;
623 process_info
->thread_handle
= NULL
;
624 /* It appears that there's no way to get the pid from a
625 * process handle before windows xp. Really.
627 #if defined(HAVE_GETPROCESSID) && !defined(MONO_CROSS_COMPILE)
628 process_info
->pid
= GetProcessId (shellex
.hProcess
);
630 process_info
->pid
= 0;
632 process_info
->tid
= 0;
638 MonoBoolean
ves_icall_System_Diagnostics_Process_CreateProcess_internal (MonoProcessStartInfo
*proc_start_info
, HANDLE stdin_handle
, HANDLE stdout_handle
, HANDLE stderr_handle
, MonoProcInfo
*process_info
)
642 STARTUPINFO startinfo
={0};
643 PROCESS_INFORMATION procinfo
;
644 gunichar2
*shell_path
= NULL
;
645 gchar
*env_vars
= NULL
;
646 gboolean free_shell_path
= TRUE
;
648 MonoString
*cmd
= proc_start_info
->arguments
;
649 guint32 creation_flags
, logon_flags
;
651 startinfo
.cb
=sizeof(STARTUPINFO
);
652 startinfo
.dwFlags
=STARTF_USESTDHANDLES
;
653 startinfo
.hStdInput
=stdin_handle
;
654 startinfo
.hStdOutput
=stdout_handle
;
655 startinfo
.hStdError
=stderr_handle
;
657 creation_flags
= CREATE_UNICODE_ENVIRONMENT
;
658 if (proc_start_info
->create_no_window
)
659 creation_flags
|= CREATE_NO_WINDOW
;
661 shell_path
= mono_string_chars (proc_start_info
->filename
);
662 complete_path (shell_path
, &spath
);
664 process_info
->pid
= -ERROR_FILE_NOT_FOUND
;
668 /* Seems like our CreateProcess does not work as the windows one.
669 * This hack is needed to deal with paths containing spaces */
671 free_shell_path
= FALSE
;
674 tmp
= mono_string_to_utf8 (cmd
);
675 newcmd
= g_strdup_printf ("%s %s", spath
, tmp
);
676 cmd
= mono_string_new_wrapper (newcmd
);
681 cmd
= mono_string_new_wrapper (spath
);
684 shell_path
= g_utf8_to_utf16 (spath
, -1, NULL
, NULL
, NULL
);
688 if (process_info
->env_keys
!= NULL
) {
691 MonoString
*key
, *value
;
692 gunichar2
*str
, *ptr
;
695 for (len
= 0, i
= 0; i
< mono_array_length (process_info
->env_keys
); i
++) {
696 ms
= mono_array_get (process_info
->env_values
, MonoString
*, i
);
700 len
+= mono_string_length (ms
) * sizeof (gunichar2
);
701 ms
= mono_array_get (process_info
->env_keys
, MonoString
*, i
);
702 len
+= mono_string_length (ms
) * sizeof (gunichar2
);
703 len
+= 2 * sizeof (gunichar2
);
706 equals16
= g_utf8_to_utf16 ("=", 1, NULL
, NULL
, NULL
);
707 ptr
= str
= g_new0 (gunichar2
, len
+ 1);
708 for (i
= 0; i
< mono_array_length (process_info
->env_keys
); i
++) {
709 value
= mono_array_get (process_info
->env_values
, MonoString
*, i
);
713 key
= mono_array_get (process_info
->env_keys
, MonoString
*, i
);
714 memcpy (ptr
, mono_string_chars (key
), mono_string_length (key
) * sizeof (gunichar2
));
715 ptr
+= mono_string_length (key
);
717 memcpy (ptr
, equals16
, sizeof (gunichar2
));
720 memcpy (ptr
, mono_string_chars (value
), mono_string_length (value
) * sizeof (gunichar2
));
721 ptr
+= mono_string_length (value
);
726 env_vars
= (gchar
*) str
;
729 /* The default dir name is "". Turn that into NULL to mean
730 * "current directory"
732 if(mono_string_length (proc_start_info
->working_directory
)==0) {
735 dir
=mono_string_chars (proc_start_info
->working_directory
);
738 if (process_info
->username
) {
739 logon_flags
= process_info
->load_user_profile
? LOGON_WITH_PROFILE
: 0;
740 ret
=CreateProcessWithLogonW (mono_string_chars (process_info
->username
), process_info
->domain
? mono_string_chars (process_info
->domain
) : NULL
, process_info
->password
, logon_flags
, shell_path
, cmd
? mono_string_chars (cmd
): NULL
, creation_flags
, env_vars
, dir
, &startinfo
, &procinfo
);
742 ret
=CreateProcess (shell_path
, cmd
? mono_string_chars (cmd
): NULL
, NULL
, NULL
, TRUE
, creation_flags
, env_vars
, dir
, &startinfo
, &procinfo
);
750 process_info
->process_handle
=procinfo
.hProcess
;
751 /*process_info->thread_handle=procinfo.hThread;*/
752 process_info
->thread_handle
=NULL
;
753 if (procinfo
.hThread
!= NULL
&& procinfo
.hThread
!= INVALID_HANDLE_VALUE
)
754 CloseHandle(procinfo
.hThread
);
755 process_info
->pid
=procinfo
.dwProcessId
;
756 process_info
->tid
=procinfo
.dwThreadId
;
758 process_info
->pid
= -GetLastError ();
764 MonoBoolean
ves_icall_System_Diagnostics_Process_WaitForExit_internal (MonoObject
*this, HANDLE process
, gint32 ms
)
772 ret
=WaitForSingleObjectEx (process
, INFINITE
, TRUE
);
774 ret
=WaitForSingleObjectEx (process
, ms
, TRUE
);
776 if(ret
==WAIT_OBJECT_0
) {
783 MonoBoolean
ves_icall_System_Diagnostics_Process_WaitForInputIdle_internal (MonoObject
*this, HANDLE process
, gint32 ms
)
791 ret
=WaitForInputIdle (process
, INFINITE
);
793 ret
=WaitForInputIdle (process
, ms
);
796 return (ret
) ? FALSE
: TRUE
;
799 gint64
ves_icall_System_Diagnostics_Process_ExitTime_internal (HANDLE process
)
803 FILETIME create_time
, exit_time
, kernel_time
, user_time
;
807 ret
=GetProcessTimes (process
, &create_time
, &exit_time
, &kernel_time
,
810 ticks
=((guint64
)exit_time
.dwHighDateTime
<< 32) +
811 exit_time
.dwLowDateTime
;
819 gint64
ves_icall_System_Diagnostics_Process_StartTime_internal (HANDLE process
)
823 FILETIME create_time
, exit_time
, kernel_time
, user_time
;
827 ret
=GetProcessTimes (process
, &create_time
, &exit_time
, &kernel_time
,
830 ticks
=((guint64
)create_time
.dwHighDateTime
<< 32) +
831 create_time
.dwLowDateTime
;
839 gint32
ves_icall_System_Diagnostics_Process_ExitCode_internal (HANDLE process
)
845 GetExitCodeProcess (process
, &code
);
847 LOGDEBUG (g_message ("%s: process exit code is %d", __func__
, code
));
852 MonoString
*ves_icall_System_Diagnostics_Process_ProcessName_internal (HANDLE process
)
857 gunichar2 name
[MAX_PATH
];
863 ok
=EnumProcessModules (process
, &mod
, sizeof(mod
), &needed
);
868 len
=GetModuleBaseName (process
, mod
, name
, MAX_PATH
);
873 LOGDEBUG (g_message ("%s: process name is [%s]", __func__
, g_utf16_to_utf8 (name
, -1, NULL
, NULL
, NULL
)));
875 string
=mono_string_new_utf16 (mono_domain_get (), name
, len
);
880 /* Returns an array of pids */
881 MonoArray
*ves_icall_System_Diagnostics_Process_GetProcesses_internal (void)
891 ret
=EnumProcesses (pids
, sizeof(pids
), &needed
);
893 /* FIXME: throw an exception */
897 count
=needed
/sizeof(DWORD
);
898 procs
=mono_array_new (mono_domain_get (), mono_get_int32_class (),
900 for(i
=0; i
<count
; i
++) {
901 mono_array_set (procs
, guint32
, i
, pids
[i
]);
907 MonoBoolean
ves_icall_System_Diagnostics_Process_GetWorkingSet_internal (HANDLE process
, guint32
*min
, guint32
*max
)
910 SIZE_T ws_min
, ws_max
;
914 ret
=GetProcessWorkingSetSize (process
, &ws_min
, &ws_max
);
915 *min
=(guint32
)ws_min
;
916 *max
=(guint32
)ws_max
;
921 MonoBoolean
ves_icall_System_Diagnostics_Process_SetWorkingSet_internal (HANDLE process
, guint32 min
, guint32 max
, MonoBoolean use_min
)
929 ret
=GetProcessWorkingSetSize (process
, &ws_min
, &ws_max
);
940 ret
=SetProcessWorkingSetSize (process
, ws_min
, ws_max
);
946 ves_icall_System_Diagnostics_Process_Kill_internal (HANDLE process
, gint32 sig
)
950 /* sig == 1 -> Kill, sig == 2 -> CloseMainWindow */
952 return TerminateProcess (process
, -sig
);
956 ves_icall_System_Diagnostics_Process_Times (HANDLE process
, gint32 type
)
958 FILETIME create_time
, exit_time
, kernel_time
, user_time
;
960 if (GetProcessTimes (process
, &create_time
, &exit_time
, &kernel_time
, &user_time
)) {
962 return *(gint64
*)&user_time
;
964 return *(gint64
*)&kernel_time
;
965 /* system + user time: FILETIME can be (memory) cast to a 64 bit int */
966 return *(gint64
*)&kernel_time
+ *(gint64
*)&user_time
;
972 ves_icall_System_Diagnostics_Process_GetPriorityClass (HANDLE process
, gint32
*error
)
974 gint32 ret
= GetPriorityClass (process
);
975 *error
= ret
== 0 ? GetLastError () : 0;
980 ves_icall_System_Diagnostics_Process_SetPriorityClass (HANDLE process
, gint32 priority_class
, gint32
*error
)
982 gboolean ret
= SetPriorityClass (process
, priority_class
);
983 *error
= ret
== 0 ? GetLastError () : 0;
988 ves_icall_System_Diagnostics_Process_ProcessHandle_duplicate (HANDLE process
)
994 LOGDEBUG (g_message ("%s: Duplicating process handle %p", __func__
, process
));
996 DuplicateHandle (GetCurrentProcess (), process
, GetCurrentProcess (),
997 &ret
, THREAD_ALL_ACCESS
, TRUE
, 0);
1003 ves_icall_System_Diagnostics_Process_ProcessHandle_close (HANDLE process
)
1005 MONO_ARCH_SAVE_REGS
;
1007 LOGDEBUG (g_message ("%s: Closing process handle %p", __func__
, process
));
1009 CloseHandle (process
);
1013 ves_icall_System_Diagnostics_Process_GetProcessData (int pid
, gint32 data_type
, gint32
*error
)
1015 MonoProcessError perror
;
1018 res
= mono_process_get_data_with_error (GINT_TO_POINTER (pid
), data_type
, &perror
);