2008-01-09 Dick Porter <dick@ximian.com>
[mono.git] / mono / metadata / process.c
1 /*
2  * process.c: System.Diagnostics.Process support
3  *
4  * Author:
5  *      Dick Porter (dick@ximian.com)
6  *
7  * (C) 2002 Ximian, Inc.
8  * Copyright (c) 2002-2006 Novell, Inc.
9  */
10
11 #include <config.h>
12
13 #include <glib.h>
14 #include <string.h>
15
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/io-layer/io-layer.h>
25 /* FIXME: fix this code to not depend so much on the inetrnals */
26 #include <mono/metadata/class-internals.h>
27
28 #undef DEBUG
29
30 HANDLE ves_icall_System_Diagnostics_Process_GetProcess_internal (guint32 pid)
31 {
32         HANDLE handle;
33         
34         MONO_ARCH_SAVE_REGS;
35
36         /* GetCurrentProcess returns a pseudo-handle, so use
37          * OpenProcess instead
38          */
39         handle=OpenProcess (PROCESS_ALL_ACCESS, TRUE, pid);
40         
41         if(handle==NULL) {
42                 /* FIXME: Throw an exception */
43                 return(NULL);
44         }
45         
46         return(handle);
47 }
48
49 guint32 ves_icall_System_Diagnostics_Process_GetPid_internal (void)
50 {
51         MONO_ARCH_SAVE_REGS;
52
53         return(GetCurrentProcessId ());
54 }
55
56 void ves_icall_System_Diagnostics_Process_Process_free_internal (MonoObject *this,
57                                                                  HANDLE process)
58 {
59         MONO_ARCH_SAVE_REGS;
60
61 #ifdef THREAD_DEBUG
62         g_message (G_GNUC_PRETTY_FUNCTION ": Closing process %p, handle %p",
63                    this, process);
64 #endif
65
66         CloseHandle (process);
67 }
68
69 #define STASH_SYS_ASS(this) \
70         if(system_assembly == NULL) { \
71                 system_assembly=this->vtable->klass->image; \
72         }
73
74 static MonoImage *system_assembly=NULL;
75
76 static guint32 unicode_chars (const gunichar2 *str)
77 {
78         guint32 len=0;
79         
80         do {
81                 if(str[len]=='\0') {
82                         return(len);
83                 }
84                 len++;
85         } while(1);
86 }
87
88 static void process_set_field_object (MonoObject *obj, const gchar *fieldname,
89                                       MonoObject *data)
90 {
91         MonoClassField *field;
92
93 #ifdef DEBUG
94         g_message (G_GNUC_PRETTY_FUNCTION ": Setting field %s to object at %p",
95                    fieldname, data);
96 #endif
97
98         field=mono_class_get_field_from_name (mono_object_class (obj),
99                                               fieldname);
100         /* FIXME: moving GC */
101         *(MonoObject **)(((char *)obj) + field->offset)=data;
102 }
103
104 static void process_set_field_string (MonoObject *obj, const gchar *fieldname,
105                                       const gunichar2 *val, guint32 len)
106 {
107         MonoClassField *field;
108         MonoString *string;
109
110 #ifdef DEBUG
111         g_message (G_GNUC_PRETTY_FUNCTION ": Setting field %s to [%s]",
112                    fieldname, g_utf16_to_utf8 (val, len, NULL, NULL, NULL));
113 #endif
114
115         string=mono_string_new_utf16 (mono_object_domain (obj), val, len);
116         
117         field=mono_class_get_field_from_name (mono_object_class (obj),
118                                               fieldname);
119         /* FIXME: moving GC */
120         *(MonoString **)(((char *)obj) + field->offset)=string;
121 }
122
123 static void process_set_field_int (MonoObject *obj, const gchar *fieldname,
124                                    guint32 val)
125 {
126         MonoClassField *field;
127
128 #ifdef DEBUG
129         g_message (G_GNUC_PRETTY_FUNCTION ": Setting field %s to %d",
130                    fieldname, val);
131 #endif
132         
133         field=mono_class_get_field_from_name (mono_object_class (obj),
134                                               fieldname);
135         *(guint32 *)(((char *)obj) + field->offset)=val;
136 }
137
138 static void process_set_field_intptr (MonoObject *obj, const gchar *fieldname,
139                                       gpointer val)
140 {
141         MonoClassField *field;
142
143 #ifdef DEBUG
144         g_message ("%s: Setting field %s to %p", __func__, fieldname, val);
145 #endif
146         
147         field=mono_class_get_field_from_name (mono_object_class (obj),
148                                               fieldname);
149         *(gpointer *)(((char *)obj) + field->offset)=val;
150 }
151
152 static void process_set_field_bool (MonoObject *obj, const gchar *fieldname,
153                                     gboolean val)
154 {
155         MonoClassField *field;
156
157 #ifdef DEBUG
158         g_message (G_GNUC_PRETTY_FUNCTION ": Setting field %s to %s",
159                    fieldname, val?"TRUE":"FALSE");
160 #endif
161         
162         field=mono_class_get_field_from_name (mono_object_class (obj),
163                                               fieldname);
164         *(guint8 *)(((char *)obj) + field->offset)=val;
165 }
166
167 #define SFI_COMMENTS            "\\StringFileInfo\\%02X%02X%02X%02X\\Comments"
168 #define SFI_COMPANYNAME         "\\StringFileInfo\\%02X%02X%02X%02X\\CompanyName"
169 #define SFI_FILEDESCRIPTION     "\\StringFileInfo\\%02X%02X%02X%02X\\FileDescription"
170 #define SFI_FILEVERSION         "\\StringFileInfo\\%02X%02X%02X%02X\\FileVersion"
171 #define SFI_INTERNALNAME        "\\StringFileInfo\\%02X%02X%02X%02X\\InternalName"
172 #define SFI_LEGALCOPYRIGHT      "\\StringFileInfo\\%02X%02X%02X%02X\\LegalCopyright"
173 #define SFI_LEGALTRADEMARKS     "\\StringFileInfo\\%02X%02X%02X%02X\\LegalTrademarks"
174 #define SFI_ORIGINALFILENAME    "\\StringFileInfo\\%02X%02X%02X%02X\\OriginalFilename"
175 #define SFI_PRIVATEBUILD        "\\StringFileInfo\\%02X%02X%02X%02X\\PrivateBuild"
176 #define SFI_PRODUCTNAME         "\\StringFileInfo\\%02X%02X%02X%02X\\ProductName"
177 #define SFI_PRODUCTVERSION      "\\StringFileInfo\\%02X%02X%02X%02X\\ProductVersion"
178 #define SFI_SPECIALBUILD        "\\StringFileInfo\\%02X%02X%02X%02X\\SpecialBuild"
179
180 static void process_module_string_read (MonoObject *filever, gpointer data,
181                                         const gchar *fieldname,
182                                         guchar lang_hi, guchar lang_lo,
183                                         const gchar *key)
184 {
185         gchar *lang_key_utf8;
186         gunichar2 *lang_key, *buffer;
187         UINT chars;
188
189         lang_key_utf8 = g_strdup_printf (key, lang_lo, lang_hi, 0x04, 0xb0);
190
191 #ifdef DEBUG
192         g_message ("%s: asking for [%s]", __func__, lang_key_utf8);
193 #endif
194
195         lang_key = g_utf8_to_utf16 (lang_key_utf8, -1, NULL, NULL, NULL);
196
197         if (VerQueryValue (data, lang_key, (gpointer *)&buffer, &chars)) {
198 #ifdef DEBUG
199                 g_message ("%s: found %d chars of [%s]", __func__, chars,
200                            g_utf16_to_utf8 (buffer, chars, NULL, NULL, NULL));
201 #endif
202                 process_set_field_string (filever, fieldname, buffer, chars);
203         }
204
205         g_free (lang_key);
206         g_free (lang_key_utf8);
207 }
208
209 static void process_module_stringtable (MonoObject *filever, gpointer data,
210                                         guchar lang_hi, guchar lang_lo)
211 {
212         process_module_string_read (filever, data, "comments", lang_hi, lang_lo,
213                                     SFI_COMMENTS);
214         process_module_string_read (filever, data, "companyname", lang_hi,
215                                     lang_lo, SFI_COMPANYNAME);
216         process_module_string_read (filever, data, "filedescription", lang_hi,
217                                     lang_lo, SFI_FILEDESCRIPTION);
218         process_module_string_read (filever, data, "fileversion", lang_hi,
219                                     lang_lo, SFI_FILEVERSION);
220         process_module_string_read (filever, data, "internalname", lang_hi,
221                                     lang_lo, SFI_INTERNALNAME);
222         process_module_string_read (filever, data, "legalcopyright", lang_hi,
223                                     lang_lo, SFI_LEGALCOPYRIGHT);
224         process_module_string_read (filever, data, "legaltrademarks", lang_hi,
225                                     lang_lo, SFI_LEGALTRADEMARKS);
226         process_module_string_read (filever, data, "originalfilename", lang_hi,
227                                     lang_lo, SFI_ORIGINALFILENAME);
228         process_module_string_read (filever, data, "privatebuild", lang_hi,
229                                     lang_lo, SFI_PRIVATEBUILD);
230         process_module_string_read (filever, data, "productname", lang_hi,
231                                     lang_lo, SFI_PRODUCTNAME);
232         process_module_string_read (filever, data, "productversion", lang_hi,
233                                     lang_lo, SFI_PRODUCTVERSION);
234         process_module_string_read (filever, data, "specialbuild", lang_hi,
235                                     lang_lo, SFI_SPECIALBUILD);
236 }
237
238 static void process_get_fileversion (MonoObject *filever, gunichar2 *filename)
239 {
240         DWORD verinfohandle;
241         VS_FIXEDFILEINFO *ffi;
242         gpointer data;
243         DWORD datalen;
244         guchar *trans_data;
245         gunichar2 *query;
246         UINT ffi_size, trans_size;
247         BOOL ok;
248         int i;
249
250         datalen = GetFileVersionInfoSize (filename, &verinfohandle);
251         if (datalen) {
252                 data = g_malloc0 (datalen);
253                 ok = GetFileVersionInfo (filename, verinfohandle, datalen,
254                                          data);
255                 if (ok) {
256                         query = g_utf8_to_utf16 ("\\", -1, NULL, NULL, NULL);
257                         if (query == NULL) {
258                                 g_free (data);
259                                 return;
260                         }
261                         
262                         if (VerQueryValue (data, query, (gpointer *)&ffi,
263                             &ffi_size)) {
264 #ifdef DEBUG
265                                 g_message (G_GNUC_PRETTY_FUNCTION ": recording assembly: FileName [%s] FileVersionInfo [%d.%d.%d.%d]", g_utf16_to_utf8 (filename, -1, NULL, NULL, NULL), HIWORD (ffi->dwFileVersionMS), LOWORD (ffi->dwFileVersionMS), HIWORD (ffi->dwFileVersionLS), LOWORD (ffi->dwFileVersionLS));
266 #endif
267         
268                                 process_set_field_int (filever, "filemajorpart", HIWORD (ffi->dwFileVersionMS));
269                                 process_set_field_int (filever, "fileminorpart", LOWORD (ffi->dwFileVersionMS));
270                                 process_set_field_int (filever, "filebuildpart", HIWORD (ffi->dwFileVersionLS));
271                                 process_set_field_int (filever, "fileprivatepart", LOWORD (ffi->dwFileVersionLS));
272
273                                 process_set_field_int (filever, "productmajorpart", HIWORD (ffi->dwProductVersionMS));
274                                 process_set_field_int (filever, "productminorpart", LOWORD (ffi->dwProductVersionMS));
275                                 process_set_field_int (filever, "productbuildpart", HIWORD (ffi->dwProductVersionLS));
276                                 process_set_field_int (filever, "productprivatepart", LOWORD (ffi->dwProductVersionLS));
277
278                                 process_set_field_bool (filever, "isdebug", (ffi->dwFileFlags & ffi->dwFileFlagsMask) & VS_FF_DEBUG);
279                                 process_set_field_bool (filever, "isprerelease", (ffi->dwFileFlags & ffi->dwFileFlagsMask) & VS_FF_PRERELEASE);
280                                 process_set_field_bool (filever, "ispatched", (ffi->dwFileFlags & ffi->dwFileFlagsMask) & VS_FF_PATCHED);
281                                 process_set_field_bool (filever, "isprivatebuild", (ffi->dwFileFlags & ffi->dwFileFlagsMask) & VS_FF_PRIVATEBUILD);
282                                 process_set_field_bool (filever, "isspecialbuild", (ffi->dwFileFlags & ffi->dwFileFlagsMask) & VS_FF_SPECIALBUILD);
283                         }
284                         g_free (query);
285
286                         query = g_utf8_to_utf16 ("\\VarFileInfo\\Translation", -1, NULL, NULL, NULL);
287                         if (query == NULL) {
288                                 g_free (data);
289                                 return;
290                         }
291                         
292                         if (VerQueryValue (data, query,
293                                            (gpointer *)&trans_data,
294                                            &trans_size)) {
295                                 /* Look for neutral or en_US language data
296                                  * (or should we use the first language ID we
297                                  * see?)
298                                  */
299                                 for (i = 0; i < trans_size; i += 4) {
300 #ifdef DEBUG
301                                         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[i], trans_data[i+1], trans_data[i+2], trans_data[i+3]);
302 #endif
303
304                                         if ((trans_data[i] == 0x09 &&
305                                              trans_data[i+1] == 0x04 &&
306                                              trans_data[i+2] == 0xb0 &&
307                                              trans_data[i+3] == 0x04) ||
308                                             (trans_data[i] == 0x00 &&
309                                              trans_data[i+1] == 0x00 &&
310                                              trans_data[i+2] == 0xb0 &&
311                                              trans_data[i+3] == 0x04) ||
312                                             (trans_data[i] == 0x7f &&
313                                              trans_data[i+1] == 0x00 &&
314                                              trans_data[i+2] == 0xb0 &&
315                                              trans_data[i+3] == 0x04)) {
316                                                 gunichar2 lang_buf[128];
317                                                 guint32 lang, lang_count;
318
319                                                 lang = (trans_data[i]) |
320                                                        (trans_data[i+1] << 8) |
321                                                        (trans_data[i+2] << 16) |
322                                                        (trans_data[i+3] << 24);
323                                                 lang_count = VerLanguageName (lang, lang_buf, 128);
324                                                 if (lang_count) {
325                                                         process_set_field_string (filever, "language", lang_buf, lang_count);
326                                                 }
327                                                 process_module_stringtable (filever, data, trans_data[i], trans_data[i+1]);
328                                         }
329                                 }
330                         }
331                         g_free (query);
332                 }
333                 g_free (data);
334         }
335 }
336
337 static void process_add_module (GPtrArray *modules, HANDLE process, HMODULE mod,
338                                 gunichar2 *filename, gunichar2 *modulename)
339 {
340         MonoClass *proc_class, *filever_class;
341         MonoObject *item, *filever;
342         MonoDomain *domain=mono_domain_get ();
343         MODULEINFO modinfo;
344         BOOL ok;
345         
346         /* Build a System.Diagnostics.ProcessModule with the data.
347          */
348         proc_class=mono_class_from_name (system_assembly, "System.Diagnostics",
349                                          "ProcessModule");
350         item=mono_object_new (domain, proc_class);
351
352         filever_class=mono_class_from_name (system_assembly,
353                                             "System.Diagnostics",
354                                             "FileVersionInfo");
355         filever=mono_object_new (domain, filever_class);
356
357         process_get_fileversion (filever, filename);
358
359         process_set_field_string (filever, "filename", filename,
360                                   unicode_chars (filename));
361
362         ok = GetModuleInformation (process, mod, &modinfo, sizeof(MODULEINFO));
363         if (ok) {
364                 process_set_field_intptr (item, "baseaddr",
365                                           modinfo.lpBaseOfDll);
366                 process_set_field_intptr (item, "entryaddr",
367                                           modinfo.EntryPoint);
368                 process_set_field_int (item, "memory_size",
369                                        modinfo.SizeOfImage);
370         }
371         process_set_field_string (item, "filename", filename,
372                                   unicode_chars (filename));
373         process_set_field_string (item, "modulename", modulename,
374                                   unicode_chars (modulename));
375         process_set_field_object (item, "version_info", filever);
376
377         /* FIXME: moving GC */
378         g_ptr_array_add (modules, item);
379 }
380
381 /* Returns an array of System.Diagnostics.ProcessModule */
382 MonoArray *ves_icall_System_Diagnostics_Process_GetModules_internal (MonoObject *this, HANDLE process)
383 {
384         GPtrArray *modules_list=g_ptr_array_new ();
385         MonoArray *arr;
386         HMODULE mods[1024];
387         gunichar2 filename[MAX_PATH];
388         gunichar2 modname[MAX_PATH];
389         DWORD needed;
390         guint32 count;
391         guint32 i;
392         
393         MONO_ARCH_SAVE_REGS;
394
395         STASH_SYS_ASS (this);
396
397         if (EnumProcessModules (process, mods, sizeof(mods), &needed)) {
398                 count = needed / sizeof(HMODULE);
399                 for (i = 0; i < count; i++) {
400                         if (GetModuleBaseName (process, mods[i], modname,
401                                                MAX_PATH) &&
402                             GetModuleFileNameEx (process, mods[i], filename,
403                                                  MAX_PATH)) {
404                                 process_add_module (modules_list, process,
405                                                     mods[i], filename, modname);
406                         }
407                 }
408         }
409
410         /* Build a MonoArray out of modules_list */
411         arr=mono_array_new (mono_domain_get (), mono_get_object_class (),
412                             modules_list->len);
413         
414         for(i=0; i<modules_list->len; i++) {
415                 mono_array_setref (arr, i, g_ptr_array_index (modules_list, i));
416         }
417         
418         g_ptr_array_free (modules_list, TRUE);
419         
420         return(arr);
421 }
422
423 void ves_icall_System_Diagnostics_FileVersionInfo_GetVersionInfo_internal (MonoObject *this, MonoString *filename)
424 {
425         MONO_ARCH_SAVE_REGS;
426
427         STASH_SYS_ASS (this);
428         
429         process_get_fileversion (this, mono_string_chars (filename));
430         process_set_field_string (this, "filename",
431                                   mono_string_chars (filename),
432                                   mono_string_length (filename));
433 }
434
435 /* Only used when UseShellExecute is false */
436 static gchar *
437 quote_path (const gchar *path)
438 {
439         gchar *res = g_shell_quote (path);
440 #ifdef PLATFORM_WIN32
441         {
442         gchar *q = res;
443         while (*q) {
444                 if (*q == '\'')
445                         *q = '\"';
446                 q++;
447         }
448         }
449 #endif
450         return res;
451 }
452
453 /* Only used when UseShellExecute is false */
454 static gboolean
455 complete_path (const gunichar2 *appname, gchar **completed)
456 {
457         gchar *utf8app;
458         gchar *found;
459
460         utf8app = g_utf16_to_utf8 (appname, -1, NULL, NULL, NULL);
461         if (g_path_is_absolute (utf8app)) {
462                 *completed = quote_path (utf8app);
463                 g_free (utf8app);
464                 return TRUE;
465         }
466
467         if (g_file_test (utf8app, G_FILE_TEST_IS_EXECUTABLE) && !g_file_test (utf8app, G_FILE_TEST_IS_DIR)) {
468                 *completed = quote_path (utf8app);
469                 g_free (utf8app);
470                 return TRUE;
471         }
472         
473         found = g_find_program_in_path (utf8app);
474         if (found == NULL) {
475                 *completed = NULL;
476                 g_free (utf8app);
477                 return FALSE;
478         }
479
480         *completed = quote_path (found);
481         g_free (found);
482         g_free (utf8app);
483         return TRUE;
484 }
485
486 #ifndef HAVE_GETPROCESSID
487 /* Run-time GetProcessId detection for Windows */
488 #ifdef PLATFORM_WIN32
489 #define HAVE_GETPROCESSID
490
491 typedef DWORD (WINAPI *GETPROCESSID_PROC) (HANDLE);
492 typedef DWORD (WINAPI *NTQUERYINFORMATIONPROCESS_PROC) (HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG);
493 typedef DWORD (WINAPI *RTLNTSTATUSTODOSERROR_PROC) (NTSTATUS);
494
495 static DWORD WINAPI GetProcessId_detect (HANDLE process);
496
497 static GETPROCESSID_PROC GetProcessId = &GetProcessId_detect;
498 static NTQUERYINFORMATIONPROCESS_PROC NtQueryInformationProcess_proc = NULL;
499 static RTLNTSTATUSTODOSERROR_PROC RtlNtStatusToDosError_proc = NULL;
500
501 static DWORD WINAPI GetProcessId_ntdll (HANDLE process)
502 {
503         PROCESS_BASIC_INFORMATION pi;
504         NTSTATUS status;
505
506         status = NtQueryInformationProcess_proc (process, ProcessBasicInformation, &pi, sizeof (pi), NULL);
507         if (NT_SUCCESS (status)) {
508                 return pi.UniqueProcessId;
509         } else {
510                 SetLastError (RtlNtStatusToDosError_proc (status));
511                 return 0;
512         }
513 }
514
515 static DWORD WINAPI GetProcessId_stub (HANDLE process)
516 {
517         SetLastError (ERROR_CALL_NOT_IMPLEMENTED);
518         return 0;
519 }
520
521 static DWORD WINAPI GetProcessId_detect (HANDLE process)
522 {
523         HMODULE module_handle;
524         GETPROCESSID_PROC GetProcessId_kernel;
525
526         /* Windows XP SP1 and above have GetProcessId API */
527         module_handle = GetModuleHandle (L"kernel32.dll");
528         if (module_handle != NULL) {
529                 GetProcessId_kernel = (GETPROCESSID_PROC) GetProcAddress (module_handle, "GetProcessId");
530                 if (GetProcessId_kernel != NULL) {
531                         GetProcessId = GetProcessId_kernel;
532                         return GetProcessId (process);
533                 }
534         }
535
536         /* Windows 2000 and above have deprecated NtQueryInformationProcess API */
537         module_handle = GetModuleHandle (L"ntdll.dll");
538         if (module_handle != NULL) {
539                 NtQueryInformationProcess_proc = (NTQUERYINFORMATIONPROCESS_PROC) GetProcAddress (module_handle, "NtQueryInformationProcess");
540                 if (NtQueryInformationProcess_proc != NULL) {
541                         RtlNtStatusToDosError_proc = (RTLNTSTATUSTODOSERROR_PROC) GetProcAddress (module_handle, "RtlNtStatusToDosError");
542                         if (RtlNtStatusToDosError_proc != NULL) {
543                                 GetProcessId = &GetProcessId_ntdll;
544                                 return GetProcessId (process);
545                         }
546                 }
547         }
548
549         /* Fall back to ERROR_CALL_NOT_IMPLEMENTED */
550         GetProcessId = &GetProcessId_stub;
551         return GetProcessId (process);
552 }
553 #endif /* PLATFORM_WIN32 */
554 #endif /* !HAVE_GETPROCESSID */
555
556 MonoBoolean ves_icall_System_Diagnostics_Process_ShellExecuteEx_internal (MonoProcessStartInfo *proc_start_info, MonoProcInfo *process_info)
557 {
558         SHELLEXECUTEINFO shellex = {0};
559         gboolean ret;
560
561         shellex.cbSize = sizeof(SHELLEXECUTEINFO);
562         shellex.fMask = SEE_MASK_FLAG_DDEWAIT | SEE_MASK_NOCLOSEPROCESS | SEE_MASK_UNICODE;
563         shellex.nShow = SW_SHOWNORMAL;
564
565         
566         
567         if (proc_start_info->filename != NULL) {
568                 shellex.lpFile = mono_string_chars (proc_start_info->filename);
569         }
570
571         if (proc_start_info->arguments != NULL) {
572                 shellex.lpParameters = mono_string_chars (proc_start_info->arguments);
573         }
574
575         if (proc_start_info->verb != NULL &&
576             mono_string_length (proc_start_info->verb) != 0) {
577                 shellex.lpVerb = mono_string_chars (proc_start_info->verb);
578         }
579
580         if (proc_start_info->working_directory != NULL &&
581             mono_string_length (proc_start_info->working_directory) != 0) {
582                 shellex.lpDirectory = mono_string_chars (proc_start_info->working_directory);
583         }
584
585         if (proc_start_info->error_dialog) {    
586                 shellex.hwnd = proc_start_info->error_dialog_parent_handle;
587         } else {
588                 shellex.fMask |= SEE_MASK_FLAG_NO_UI;
589         }
590
591         ret = ShellExecuteEx (&shellex);
592         if (ret == FALSE) {
593                 process_info->pid = -GetLastError ();
594         } else {
595                 process_info->process_handle = shellex.hProcess;
596                 process_info->thread_handle = NULL;
597                 /* It appears that there's no way to get the pid from a
598                  * process handle before windows xp.  Really.
599                  */
600 #ifdef HAVE_GETPROCESSID
601                 process_info->pid = GetProcessId (shellex.hProcess);
602 #else
603                 process_info->pid = 0;
604 #endif
605                 process_info->tid = 0;
606         }
607
608         return (ret);
609 }
610
611 MonoBoolean ves_icall_System_Diagnostics_Process_CreateProcess_internal (MonoProcessStartInfo *proc_start_info, HANDLE stdin_handle, HANDLE stdout_handle, HANDLE stderr_handle, MonoProcInfo *process_info)
612 {
613         gboolean ret;
614         gunichar2 *dir;
615         STARTUPINFO startinfo={0};
616         PROCESS_INFORMATION procinfo;
617         gunichar2 *shell_path = NULL;
618         gchar *env_vars = NULL;
619         gboolean free_shell_path = TRUE;
620         gchar *spath = NULL;
621         MonoString *cmd = proc_start_info->arguments;
622         guint32 creation_flags, logon_flags;
623         
624         startinfo.cb=sizeof(STARTUPINFO);
625         startinfo.dwFlags=STARTF_USESTDHANDLES;
626         startinfo.hStdInput=stdin_handle;
627         startinfo.hStdOutput=stdout_handle;
628         startinfo.hStdError=stderr_handle;
629
630         creation_flags = CREATE_UNICODE_ENVIRONMENT;
631         if (proc_start_info->create_no_window)
632                 creation_flags |= CREATE_NO_WINDOW;
633         
634         shell_path = mono_string_chars (proc_start_info->filename);
635         complete_path (shell_path, &spath);
636         if (spath == NULL) {
637                 process_info->pid = -ERROR_FILE_NOT_FOUND;
638                 return FALSE;
639         }
640 #ifdef PLATFORM_WIN32
641         /* Seems like our CreateProcess does not work as the windows one.
642          * This hack is needed to deal with paths containing spaces */
643         shell_path = NULL;
644         free_shell_path = FALSE;
645         if (cmd) {
646                 gchar *newcmd, *tmp;
647                 tmp = mono_string_to_utf8 (cmd);
648                 newcmd = g_strdup_printf ("%s %s", spath, tmp);
649                 cmd = mono_string_new_wrapper (newcmd);
650                 g_free (tmp);
651                 g_free (newcmd);
652         }
653         else {
654                 cmd = mono_string_new_wrapper (spath);
655         }
656 #else
657         shell_path = g_utf8_to_utf16 (spath, -1, NULL, NULL, NULL);
658 #endif
659         g_free (spath);
660
661         if (process_info->env_keys != NULL) {
662                 gint i, len; 
663                 MonoString *ms;
664                 MonoString *key, *value;
665                 gunichar2 *str, *ptr;
666                 gunichar2 *equals16;
667
668                 for (len = 0, i = 0; i < mono_array_length (process_info->env_keys); i++) {
669                         ms = mono_array_get (process_info->env_values, MonoString *, i);
670                         if (ms == NULL)
671                                 continue;
672
673                         len += mono_string_length (ms) * sizeof (gunichar2);
674                         ms = mono_array_get (process_info->env_keys, MonoString *, i);
675                         len += mono_string_length (ms) * sizeof (gunichar2);
676                         len += 2 * sizeof (gunichar2);
677                 }
678
679                 equals16 = g_utf8_to_utf16 ("=", 1, NULL, NULL, NULL);
680                 ptr = str = g_new0 (gunichar2, len + 1);
681                 for (i = 0; i < mono_array_length (process_info->env_keys); i++) {
682                         value = mono_array_get (process_info->env_values, MonoString *, i);
683                         if (value == NULL)
684                                 continue;
685
686                         key = mono_array_get (process_info->env_keys, MonoString *, i);
687                         memcpy (ptr, mono_string_chars (key), mono_string_length (key) * sizeof (gunichar2));
688                         ptr += mono_string_length (key);
689
690                         memcpy (ptr, equals16, sizeof (gunichar2));
691                         ptr++;
692
693                         memcpy (ptr, mono_string_chars (value), mono_string_length (value) * sizeof (gunichar2));
694                         ptr += mono_string_length (value);
695                         ptr++;
696                 }
697
698                 g_free (equals16);
699                 env_vars = (gchar *) str;
700         }
701         
702         /* The default dir name is "".  Turn that into NULL to mean
703          * "current directory"
704          */
705         if(mono_string_length (proc_start_info->working_directory)==0) {
706                 dir=NULL;
707         } else {
708                 dir=mono_string_chars (proc_start_info->working_directory);
709         }
710
711         if (process_info->username) {
712                 logon_flags = process_info->load_user_profile ? LOGON_WITH_PROFILE : 0;
713                 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);
714         } else {
715                 ret=CreateProcess (shell_path, cmd? mono_string_chars (cmd): NULL, NULL, NULL, TRUE, creation_flags, env_vars, dir, &startinfo, &procinfo);
716         }
717
718         g_free (env_vars);
719         if (free_shell_path)
720                 g_free (shell_path);
721
722         if(ret) {
723                 process_info->process_handle=procinfo.hProcess;
724                 /*process_info->thread_handle=procinfo.hThread;*/
725                 process_info->thread_handle=NULL;
726                 if (procinfo.hThread != NULL && procinfo.hThread != INVALID_HANDLE_VALUE)
727                         CloseHandle(procinfo.hThread);
728                 process_info->pid=procinfo.dwProcessId;
729                 process_info->tid=procinfo.dwThreadId;
730         } else {
731                 process_info->pid = -GetLastError ();
732         }
733         
734         return(ret);
735 }
736
737 MonoBoolean ves_icall_System_Diagnostics_Process_WaitForExit_internal (MonoObject *this, HANDLE process, gint32 ms)
738 {
739         guint32 ret;
740         
741         MONO_ARCH_SAVE_REGS;
742
743         if(ms<0) {
744                 /* Wait forever */
745                 ret=WaitForSingleObjectEx (process, INFINITE, TRUE);
746         } else {
747                 ret=WaitForSingleObjectEx (process, ms, TRUE);
748         }
749         if(ret==WAIT_OBJECT_0) {
750                 return(TRUE);
751         } else {
752                 return(FALSE);
753         }
754 }
755
756 gint64 ves_icall_System_Diagnostics_Process_ExitTime_internal (HANDLE process)
757 {
758         gboolean ret;
759         gint64 ticks;
760         FILETIME create_time, exit_time, kernel_time, user_time;
761         
762         MONO_ARCH_SAVE_REGS;
763
764         ret=GetProcessTimes (process, &create_time, &exit_time, &kernel_time,
765                              &user_time);
766         if(ret==TRUE) {
767                 ticks=((guint64)exit_time.dwHighDateTime << 32) +
768                         exit_time.dwLowDateTime;
769                 
770                 return(ticks);
771         } else {
772                 return(0);
773         }
774 }
775
776 gint64 ves_icall_System_Diagnostics_Process_StartTime_internal (HANDLE process)
777 {
778         gboolean ret;
779         gint64 ticks;
780         FILETIME create_time, exit_time, kernel_time, user_time;
781         
782         MONO_ARCH_SAVE_REGS;
783
784         ret=GetProcessTimes (process, &create_time, &exit_time, &kernel_time,
785                              &user_time);
786         if(ret==TRUE) {
787                 ticks=((guint64)create_time.dwHighDateTime << 32) +
788                         create_time.dwLowDateTime;
789                 
790                 return(ticks);
791         } else {
792                 return(0);
793         }
794 }
795
796 gint32 ves_icall_System_Diagnostics_Process_ExitCode_internal (HANDLE process)
797 {
798         DWORD code;
799         
800         MONO_ARCH_SAVE_REGS;
801
802         GetExitCodeProcess (process, &code);
803         
804 #ifdef DEBUG
805         g_message (G_GNUC_PRETTY_FUNCTION ": process exit code is %d", code);
806 #endif
807         
808         return(code);
809 }
810
811 MonoString *ves_icall_System_Diagnostics_Process_ProcessName_internal (HANDLE process)
812 {
813         MonoString *string;
814         gboolean ok;
815         HMODULE mod;
816         gunichar2 name[MAX_PATH];
817         DWORD needed;
818         guint32 len;
819         
820         MONO_ARCH_SAVE_REGS;
821
822         ok=EnumProcessModules (process, &mod, sizeof(mod), &needed);
823         if(ok==FALSE) {
824                 return(NULL);
825         }
826         
827         len=GetModuleBaseName (process, mod, name, MAX_PATH);
828         if(len==0) {
829                 return(NULL);
830         }
831         
832 #ifdef DEBUG
833         g_message (G_GNUC_PRETTY_FUNCTION ": process name is [%s]",
834                    g_utf16_to_utf8 (name, -1, NULL, NULL, NULL));
835 #endif
836         
837         string=mono_string_new_utf16 (mono_domain_get (), name, len);
838         
839         return(string);
840 }
841
842 /* Returns an array of pids */
843 MonoArray *ves_icall_System_Diagnostics_Process_GetProcesses_internal (void)
844 {
845         MonoArray *procs;
846         gboolean ret;
847         DWORD needed;
848         guint32 count, i;
849         DWORD pids[1024];
850
851         MONO_ARCH_SAVE_REGS;
852
853         ret=EnumProcesses (pids, sizeof(pids), &needed);
854         if(ret==FALSE) {
855                 /* FIXME: throw an exception */
856                 return(NULL);
857         }
858         
859         count=needed/sizeof(DWORD);
860         procs=mono_array_new (mono_domain_get (), mono_get_int32_class (),
861                               count);
862         for(i=0; i<count; i++) {
863                 mono_array_set (procs, guint32, i, pids[i]);
864         }
865         
866         return(procs);
867 }
868
869 MonoBoolean ves_icall_System_Diagnostics_Process_GetWorkingSet_internal (HANDLE process, guint32 *min, guint32 *max)
870 {
871         gboolean ret;
872         SIZE_T ws_min, ws_max;
873         
874         MONO_ARCH_SAVE_REGS;
875
876         ret=GetProcessWorkingSetSize (process, &ws_min, &ws_max);
877         *min=(guint32)ws_min;
878         *max=(guint32)ws_max;
879         
880         return(ret);
881 }
882
883 MonoBoolean ves_icall_System_Diagnostics_Process_SetWorkingSet_internal (HANDLE process, guint32 min, guint32 max, MonoBoolean use_min)
884 {
885         gboolean ret;
886         SIZE_T ws_min;
887         SIZE_T ws_max;
888         
889         MONO_ARCH_SAVE_REGS;
890
891         ret=GetProcessWorkingSetSize (process, &ws_min, &ws_max);
892         if(ret==FALSE) {
893                 return(FALSE);
894         }
895         
896         if(use_min==TRUE) {
897                 ws_min=(SIZE_T)min;
898         } else {
899                 ws_max=(SIZE_T)max;
900         }
901         
902         ret=SetProcessWorkingSetSize (process, ws_min, ws_max);
903
904         return(ret);
905 }
906
907 MonoBoolean
908 ves_icall_System_Diagnostics_Process_Kill_internal (HANDLE process, gint32 sig)
909 {
910         MONO_ARCH_SAVE_REGS;
911
912         /* sig == 1 -> Kill, sig == 2 -> CloseMainWindow */
913
914         return TerminateProcess (process, -sig);
915 }
916
917 gint64
918 ves_icall_System_Diagnostics_Process_Times (HANDLE process, gint32 type)
919 {
920         FILETIME create_time, exit_time, kernel_time, user_time;
921         
922         if (GetProcessTimes (process, &create_time, &exit_time, &kernel_time, &user_time)) {
923                 if (type == 0)
924                         return *(gint64*)&user_time;
925                 else if (type == 1)
926                         return *(gint64*)&kernel_time;
927                 /* system + user time: FILETIME can be (memory) cast to a 64 bit int */
928                 return *(gint64*)&kernel_time + *(gint64*)&user_time;
929         }
930         return 0;
931 }
932
933 gint32
934 ves_icall_System_Diagnostics_Process_GetPriorityClass (HANDLE process, gint32 *error)
935 {
936         gint32 ret = GetPriorityClass (process);
937         *error = ret == 0 ? GetLastError () : 0;
938         return ret;
939 }
940
941 MonoBoolean
942 ves_icall_System_Diagnostics_Process_SetPriorityClass (HANDLE process, gint32 priority_class, gint32 *error)
943 {
944         gboolean ret = SetPriorityClass (process, priority_class);
945         *error = ret == 0 ? GetLastError () : 0;
946         return ret;
947 }