[runtime] Process.Modules with managed assemblies.
[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  * Copyright 2002 Ximian, Inc.
8  * Copyright 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-internals.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 internals */
27 #include <mono/metadata/class-internals.h>
28
29 #define LOGDEBUG(...)  
30 /* define LOGDEBUG(...) g_message(__VA_ARGS__)  */
31
32
33 HANDLE ves_icall_System_Diagnostics_Process_GetProcess_internal (guint32 pid)
34 {
35         HANDLE handle;
36         
37         /* GetCurrentProcess returns a pseudo-handle, so use
38          * OpenProcess instead
39          */
40         handle=OpenProcess (PROCESS_ALL_ACCESS, TRUE, pid);
41         
42         if(handle==NULL) {
43                 /* FIXME: Throw an exception */
44                 return(NULL);
45         }
46         
47         return(handle);
48 }
49
50 guint32
51 ves_icall_System_Diagnostics_Process_GetPid_internal (void)
52 {
53         return mono_process_current_pid ();
54 }
55
56 void ves_icall_System_Diagnostics_Process_Process_free_internal (MonoObject *this,
57                                                                  HANDLE process)
58 {
59 #ifdef THREAD_DEBUG
60         g_message ("%s: Closing process %p, handle %p", __func__, this, process);
61 #endif
62
63 #if defined(TARGET_WIN32) || defined(HOST_WIN32)
64         CloseHandle (process);
65 #else
66         CloseProcess (process);
67 #endif
68 }
69
70 #define STASH_SYS_ASS(this) \
71         if(system_assembly == NULL) { \
72                 system_assembly=this->vtable->klass->image; \
73         }
74
75 static MonoImage *system_assembly=NULL;
76
77 static guint32 unicode_chars (const gunichar2 *str)
78 {
79         guint32 len=0;
80         
81         do {
82                 if(str[len]=='\0') {
83                         return(len);
84                 }
85                 len++;
86         } while(1);
87 }
88
89 static void process_set_field_object (MonoObject *obj, const gchar *fieldname,
90                                       MonoObject *data)
91 {
92         MonoClassField *field;
93
94         LOGDEBUG (g_message ("%s: Setting field %s to object at %p", __func__, fieldname, data));
95
96         field=mono_class_get_field_from_name (mono_object_class (obj),
97                                               fieldname);
98         mono_gc_wbarrier_generic_store (((char *)obj) + field->offset, data);
99 }
100
101 static void process_set_field_string (MonoObject *obj, const gchar *fieldname,
102                                       const gunichar2 *val, guint32 len)
103 {
104         MonoClassField *field;
105         MonoString *string;
106
107         LOGDEBUG (g_message ("%s: Setting field %s to [%s]", __func__, fieldname, g_utf16_to_utf8 (val, len, NULL, NULL, NULL)));
108
109         string=mono_string_new_utf16 (mono_object_domain (obj), val, len);
110         
111         field=mono_class_get_field_from_name (mono_object_class (obj),
112                                               fieldname);
113         mono_gc_wbarrier_generic_store (((char *)obj) + field->offset, (MonoObject*)string);
114 }
115
116 static void process_set_field_string_char (MonoObject *obj, const gchar *fieldname,
117                                       const gchar *val)
118 {
119         MonoClassField *field;
120         MonoString *string;
121
122         LOGDEBUG (g_message ("%s: Setting field %s to [%s]", __func__, fieldname, val));
123
124         string=mono_string_new (mono_object_domain (obj), val);
125         
126         field=mono_class_get_field_from_name (mono_object_class (obj), fieldname);
127         mono_gc_wbarrier_generic_store (((char *)obj) + field->offset, (MonoObject*)string);
128 }
129
130 static void process_set_field_int (MonoObject *obj, const gchar *fieldname,
131                                    guint32 val)
132 {
133         MonoClassField *field;
134
135         LOGDEBUG (g_message ("%s: Setting field %s to %d", __func__,fieldname, val));
136         
137         field=mono_class_get_field_from_name (mono_object_class (obj),
138                                               fieldname);
139         *(guint32 *)(((char *)obj) + field->offset)=val;
140 }
141
142 static void process_set_field_intptr (MonoObject *obj, const gchar *fieldname,
143                                       gpointer val)
144 {
145         MonoClassField *field;
146
147         LOGDEBUG (g_message ("%s: Setting field %s to %p", __func__, fieldname, val));
148         
149         field=mono_class_get_field_from_name (mono_object_class (obj),
150                                               fieldname);
151         *(gpointer *)(((char *)obj) + field->offset)=val;
152 }
153
154 static void process_set_field_bool (MonoObject *obj, const gchar *fieldname,
155                                     gboolean val)
156 {
157         MonoClassField *field;
158
159         LOGDEBUG (g_message ("%s: Setting field %s to %s", __func__, fieldname, val?"TRUE":"FALSE"));
160         
161         field=mono_class_get_field_from_name (mono_object_class (obj),
162                                               fieldname);
163         *(guint8 *)(((char *)obj) + field->offset)=val;
164 }
165
166 #define SFI_COMMENTS            "\\StringFileInfo\\%02X%02X%02X%02X\\Comments"
167 #define SFI_COMPANYNAME         "\\StringFileInfo\\%02X%02X%02X%02X\\CompanyName"
168 #define SFI_FILEDESCRIPTION     "\\StringFileInfo\\%02X%02X%02X%02X\\FileDescription"
169 #define SFI_FILEVERSION         "\\StringFileInfo\\%02X%02X%02X%02X\\FileVersion"
170 #define SFI_INTERNALNAME        "\\StringFileInfo\\%02X%02X%02X%02X\\InternalName"
171 #define SFI_LEGALCOPYRIGHT      "\\StringFileInfo\\%02X%02X%02X%02X\\LegalCopyright"
172 #define SFI_LEGALTRADEMARKS     "\\StringFileInfo\\%02X%02X%02X%02X\\LegalTrademarks"
173 #define SFI_ORIGINALFILENAME    "\\StringFileInfo\\%02X%02X%02X%02X\\OriginalFilename"
174 #define SFI_PRIVATEBUILD        "\\StringFileInfo\\%02X%02X%02X%02X\\PrivateBuild"
175 #define SFI_PRODUCTNAME         "\\StringFileInfo\\%02X%02X%02X%02X\\ProductName"
176 #define SFI_PRODUCTVERSION      "\\StringFileInfo\\%02X%02X%02X%02X\\ProductVersion"
177 #define SFI_SPECIALBUILD        "\\StringFileInfo\\%02X%02X%02X%02X\\SpecialBuild"
178 #define EMPTY_STRING            (gunichar2*)"\000\000"
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         LOGDEBUG (g_message ("%s: asking for [%s]", __func__, lang_key_utf8));
192
193         lang_key = g_utf8_to_utf16 (lang_key_utf8, -1, NULL, NULL, NULL);
194
195         if (VerQueryValue (data, lang_key, (gpointer *)&buffer, &chars) && chars > 0) {
196                 LOGDEBUG (g_message ("%s: found %d chars of [%s]", __func__, chars, g_utf16_to_utf8 (buffer, chars, NULL, NULL, NULL)));
197                 /* chars includes trailing null */
198                 process_set_field_string (filever, fieldname, buffer, chars - 1);
199         } else {
200                 process_set_field_string (filever, fieldname, EMPTY_STRING, 0);
201         }
202
203         g_free (lang_key);
204         g_free (lang_key_utf8);
205 }
206
207 static void process_module_stringtable (MonoObject *filever, gpointer data,
208                                         guchar lang_hi, guchar lang_lo)
209 {
210         process_module_string_read (filever, data, "comments", lang_hi, lang_lo,
211                                     SFI_COMMENTS);
212         process_module_string_read (filever, data, "companyname", lang_hi,
213                                     lang_lo, SFI_COMPANYNAME);
214         process_module_string_read (filever, data, "filedescription", lang_hi,
215                                     lang_lo, SFI_FILEDESCRIPTION);
216         process_module_string_read (filever, data, "fileversion", lang_hi,
217                                     lang_lo, SFI_FILEVERSION);
218         process_module_string_read (filever, data, "internalname", lang_hi,
219                                     lang_lo, SFI_INTERNALNAME);
220         process_module_string_read (filever, data, "legalcopyright", lang_hi,
221                                     lang_lo, SFI_LEGALCOPYRIGHT);
222         process_module_string_read (filever, data, "legaltrademarks", lang_hi,
223                                     lang_lo, SFI_LEGALTRADEMARKS);
224         process_module_string_read (filever, data, "originalfilename", lang_hi,
225                                     lang_lo, SFI_ORIGINALFILENAME);
226         process_module_string_read (filever, data, "privatebuild", lang_hi,
227                                     lang_lo, SFI_PRIVATEBUILD);
228         process_module_string_read (filever, data, "productname", lang_hi,
229                                     lang_lo, SFI_PRODUCTNAME);
230         process_module_string_read (filever, data, "productversion", lang_hi,
231                                     lang_lo, SFI_PRODUCTVERSION);
232         process_module_string_read (filever, data, "specialbuild", lang_hi,
233                                     lang_lo, SFI_SPECIALBUILD);
234 }
235
236 static void process_get_fileversion (MonoObject *filever, gunichar2 *filename)
237 {
238         DWORD verinfohandle;
239         VS_FIXEDFILEINFO *ffi;
240         gpointer data;
241         DWORD datalen;
242         guchar *trans_data;
243         gunichar2 *query;
244         UINT ffi_size, trans_size;
245         BOOL ok;
246         gunichar2 lang_buf[128];
247         guint32 lang, lang_count;
248
249         datalen = GetFileVersionInfoSize (filename, &verinfohandle);
250         if (datalen) {
251                 data = g_malloc0 (datalen);
252                 ok = GetFileVersionInfo (filename, verinfohandle, datalen,
253                                          data);
254                 if (ok) {
255                         query = g_utf8_to_utf16 ("\\", -1, NULL, NULL, NULL);
256                         if (query == NULL) {
257                                 g_free (data);
258                                 return;
259                         }
260                         
261                         if (VerQueryValue (data, query, (gpointer *)&ffi,
262                             &ffi_size)) {
263                                 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)));
264         
265                                 process_set_field_int (filever, "filemajorpart", HIWORD (ffi->dwFileVersionMS));
266                                 process_set_field_int (filever, "fileminorpart", LOWORD (ffi->dwFileVersionMS));
267                                 process_set_field_int (filever, "filebuildpart", HIWORD (ffi->dwFileVersionLS));
268                                 process_set_field_int (filever, "fileprivatepart", LOWORD (ffi->dwFileVersionLS));
269
270                                 process_set_field_int (filever, "productmajorpart", HIWORD (ffi->dwProductVersionMS));
271                                 process_set_field_int (filever, "productminorpart", LOWORD (ffi->dwProductVersionMS));
272                                 process_set_field_int (filever, "productbuildpart", HIWORD (ffi->dwProductVersionLS));
273                                 process_set_field_int (filever, "productprivatepart", LOWORD (ffi->dwProductVersionLS));
274
275                                 process_set_field_bool (filever, "isdebug", ((ffi->dwFileFlags & ffi->dwFileFlagsMask) & VS_FF_DEBUG) != 0);
276                                 process_set_field_bool (filever, "isprerelease", ((ffi->dwFileFlags & ffi->dwFileFlagsMask) & VS_FF_PRERELEASE) != 0);
277                                 process_set_field_bool (filever, "ispatched", ((ffi->dwFileFlags & ffi->dwFileFlagsMask) & VS_FF_PATCHED) != 0);
278                                 process_set_field_bool (filever, "isprivatebuild", ((ffi->dwFileFlags & ffi->dwFileFlagsMask) & VS_FF_PRIVATEBUILD) != 0);
279                                 process_set_field_bool (filever, "isspecialbuild", ((ffi->dwFileFlags & ffi->dwFileFlagsMask) & VS_FF_SPECIALBUILD) != 0);
280                         }
281                         g_free (query);
282
283                         query = g_utf8_to_utf16 ("\\VarFileInfo\\Translation", -1, NULL, NULL, NULL);
284                         if (query == NULL) {
285                                 g_free (data);
286                                 return;
287                         }
288                         
289                         if (VerQueryValue (data, query,
290                                            (gpointer *)&trans_data,
291                                            &trans_size)) {
292                                 /* use the first language ID we see
293                                  */
294                                 if (trans_size >= 4) {
295                                         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]));
296                                         lang = (trans_data[0]) |
297                                                 (trans_data[1] << 8) |
298                                                 (trans_data[2] << 16) |
299                                                 (trans_data[3] << 24);
300                                         /* Only give the lower 16 bits
301                                          * to VerLanguageName, as
302                                          * Windows gets confused
303                                          * otherwise
304                                          */
305                                         lang_count = VerLanguageName (lang & 0xFFFF, lang_buf, 128);
306                                         if (lang_count) {
307                                                 process_set_field_string (filever, "language", lang_buf, lang_count);
308                                         }
309                                         process_module_stringtable (filever, data, trans_data[0], trans_data[1]);
310                                 }
311                         } else {
312                                 /* No strings, so set every field to
313                                  * the empty string
314                                  */
315                                 process_set_field_string (filever,
316                                                           "comments",
317                                                           EMPTY_STRING, 0);
318                                 process_set_field_string (filever,
319                                                           "companyname",
320                                                           EMPTY_STRING, 0);
321                                 process_set_field_string (filever,
322                                                           "filedescription",
323                                                           EMPTY_STRING, 0);
324                                 process_set_field_string (filever,
325                                                           "fileversion",
326                                                           EMPTY_STRING, 0);
327                                 process_set_field_string (filever,
328                                                           "internalname",
329                                                           EMPTY_STRING, 0);
330                                 process_set_field_string (filever,
331                                                           "legalcopyright",
332                                                           EMPTY_STRING, 0);
333                                 process_set_field_string (filever,
334                                                           "legaltrademarks",
335                                                           EMPTY_STRING, 0);
336                                 process_set_field_string (filever,
337                                                           "originalfilename",
338                                                           EMPTY_STRING, 0);
339                                 process_set_field_string (filever,
340                                                           "privatebuild",
341                                                           EMPTY_STRING, 0);
342                                 process_set_field_string (filever,
343                                                           "productname",
344                                                           EMPTY_STRING, 0);
345                                 process_set_field_string (filever,
346                                                           "productversion",
347                                                           EMPTY_STRING, 0);
348                                 process_set_field_string (filever,
349                                                           "specialbuild",
350                                                           EMPTY_STRING, 0);
351
352                                 /* And language seems to be set to
353                                  * en_US according to bug 374600
354                                  */
355                                 lang_count = VerLanguageName (0x0409, lang_buf, 128);
356                                 if (lang_count) {
357                                         process_set_field_string (filever, "language", lang_buf, lang_count);
358                                 }
359                         }
360                         
361                         g_free (query);
362                 }
363                 g_free (data);
364         }
365 }
366
367 static MonoObject* get_process_module (MonoAssembly *assembly, MonoClass *proc_class)
368 {
369         MonoClass *filever_class;
370         MonoObject *item, *filever;
371         MonoDomain *domain = mono_domain_get ();
372         char filename [80] = "[In Memory] ";
373
374         strncat (filename, assembly->image->module_name, 80);
375
376         /* Build a System.Diagnostics.ProcessModule with the data.
377          */
378         item = mono_object_new (domain, proc_class);
379
380         filever_class = mono_class_from_name (system_assembly,
381                                             "System.Diagnostics",
382                                             "FileVersionInfo");
383         filever = mono_object_new (domain, filever_class);
384
385 // TODO: Implement process_get_assembly_fileversion
386 //      process_get_assembly_fileversion (filever, assembly);
387         process_set_field_string_char (filever, "filename", filename);
388         process_set_field_object (item, "version_info", filever);
389
390         process_set_field_intptr (item, "baseaddr", assembly->image->raw_data);
391         process_set_field_int (item, "memory_size", assembly->image->raw_data_len);
392         process_set_field_string_char (item, "filename", filename);
393         process_set_field_string_char (item, "modulename", assembly->image->module_name);
394
395         return item;
396 }
397
398 static MonoObject* process_add_module (HANDLE process, HMODULE mod, gunichar2 *filename, gunichar2 *modulename, MonoClass *proc_class)
399 {
400         MonoClass *filever_class;
401         MonoObject *item, *filever;
402         MonoDomain *domain=mono_domain_get ();
403         MODULEINFO modinfo;
404         BOOL ok;
405         
406         /* Build a System.Diagnostics.ProcessModule with the data.
407          */
408         item=mono_object_new (domain, proc_class);
409
410         filever_class=mono_class_from_name (system_assembly,
411                                             "System.Diagnostics",
412                                             "FileVersionInfo");
413         filever=mono_object_new (domain, filever_class);
414
415         process_get_fileversion (filever, filename);
416
417         process_set_field_string (filever, "filename", filename,
418                                   unicode_chars (filename));
419
420         ok = GetModuleInformation (process, mod, &modinfo, sizeof(MODULEINFO));
421         if (ok) {
422                 process_set_field_intptr (item, "baseaddr",
423                                           modinfo.lpBaseOfDll);
424                 process_set_field_intptr (item, "entryaddr",
425                                           modinfo.EntryPoint);
426                 process_set_field_int (item, "memory_size",
427                                        modinfo.SizeOfImage);
428         }
429         process_set_field_string (item, "filename", filename,
430                                   unicode_chars (filename));
431         process_set_field_string (item, "modulename", modulename,
432                                   unicode_chars (modulename));
433         process_set_field_object (item, "version_info", filever);
434
435         return item;
436 }
437
438 static GPtrArray* get_domain_assemblies (MonoDomain *domain)
439 {
440         GSList *tmp;
441         GPtrArray *assemblies;
442
443         /* 
444          * Make a copy of the list of assemblies because we can't hold the assemblies
445          * lock while creating objects etc.
446          */
447         assemblies = g_ptr_array_new ();
448         mono_domain_assemblies_lock (domain);
449         for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) {
450                 MonoAssembly *ass = tmp->data;
451                 if (ass->image->fileio_used)
452                         continue;
453                 g_ptr_array_add (assemblies, ass);
454         }
455         mono_domain_assemblies_unlock (domain);
456
457         return assemblies;
458 }
459
460 /* Returns an array of System.Diagnostics.ProcessModule */
461 MonoArray *ves_icall_System_Diagnostics_Process_GetModules_internal (MonoObject *this, HANDLE process)
462 {
463         MonoArray *temp_arr = NULL;
464         MonoArray *arr;
465         HMODULE mods[1024];
466         gunichar2 filename[MAX_PATH];
467         gunichar2 modname[MAX_PATH];
468         DWORD needed;
469         guint32 count = 0, module_count = 0, assembly_count = 0;
470         guint32 i, num_added = 0;
471         MonoClass *proc_class;
472         GPtrArray *assemblies = NULL;
473         static HANDLE current_process = 0;
474         
475         if (current_process == 0) {
476                 int pid = mono_process_current_pid ();
477                 current_process = ves_icall_System_Diagnostics_Process_GetProcess_internal (pid);
478         }
479
480         STASH_SYS_ASS (this);
481
482         if (process == current_process) {
483                 assemblies = get_domain_assemblies (mono_domain_get ());
484                 assembly_count = assemblies->len;
485         }
486
487         if (EnumProcessModules (process, mods, sizeof(mods), &needed)) {
488                 module_count += needed / sizeof(HMODULE);
489         }
490
491         count = module_count + assembly_count; 
492         proc_class = mono_class_from_name (system_assembly, "System.Diagnostics", "ProcessModule");
493         temp_arr = mono_array_new (mono_domain_get (), proc_class, count);
494
495         for (i = 0; i < module_count; i++) {
496                 if (GetModuleBaseName (process, mods[i], modname, MAX_PATH) &&
497                                 GetModuleFileNameEx (process, mods[i], filename, MAX_PATH)) {
498                         MonoObject *module = process_add_module (process, mods[i],
499                                         filename, modname, proc_class);
500                         mono_array_setref (temp_arr, num_added++, module);
501                 }
502         }
503
504         if (assemblies) {
505                 for (i = 0; i < assembly_count; i++) {
506                         MonoAssembly *ass = g_ptr_array_index (assemblies, i);
507                         MonoObject *module = get_process_module (ass, proc_class);
508                         mono_array_setref (temp_arr, num_added++, module);
509                 }
510                 g_ptr_array_free (assemblies, TRUE);
511         }
512
513         if (count == num_added) {
514                 arr = temp_arr;
515         } else {
516                 /* shorter version of the array */
517                 arr = mono_array_new (mono_domain_get (), proc_class, num_added);
518
519                 for (i = 0; i < num_added; i++)
520                         mono_array_setref (arr, i, mono_array_get (temp_arr, MonoObject*, i));
521         }
522
523         if (count == num_added) {
524                 arr = temp_arr;
525         } else {
526                 /* shorter version of the array */
527                 arr = mono_array_new (mono_domain_get (), proc_class, num_added);
528
529                 for (i = 0; i < num_added; i++)
530                         mono_array_setref (arr, i, mono_array_get (temp_arr, MonoObject*, i));
531         }
532
533         return arr;
534 }
535
536 void ves_icall_System_Diagnostics_FileVersionInfo_GetVersionInfo_internal (MonoObject *this, MonoString *filename)
537 {
538         STASH_SYS_ASS (this);
539         
540         process_get_fileversion (this, mono_string_chars (filename));
541         process_set_field_string (this, "filename",
542                                   mono_string_chars (filename),
543                                   mono_string_length (filename));
544 }
545
546 /* Only used when UseShellExecute is false */
547 static gchar *
548 quote_path (const gchar *path)
549 {
550         gchar *res = g_shell_quote (path);
551 #ifdef TARGET_WIN32
552         {
553         gchar *q = res;
554         while (*q) {
555                 if (*q == '\'')
556                         *q = '\"';
557                 q++;
558         }
559         }
560 #endif
561         return res;
562 }
563
564 /* Only used when UseShellExecute is false */
565 static gboolean
566 complete_path (const gunichar2 *appname, gchar **completed)
567 {
568         gchar *utf8app, *utf8appmemory;
569         gchar *found;
570
571         utf8appmemory = utf8app = g_utf16_to_utf8 (appname, -1, NULL, NULL, NULL);
572 #ifdef TARGET_WIN32 // Should this happen on all platforms? 
573         {
574                 // remove the quotes around utf8app.
575                 size_t len;
576                 len = strlen (utf8app);
577                 if (len) {
578                         if (utf8app[len-1] == '\"')
579                                 utf8app[len-1] = '\0';
580                         if (utf8app[0] == '\"')
581                                 utf8app++;
582                 }
583         }
584 #endif
585
586         if (g_path_is_absolute (utf8app)) {
587                 *completed = quote_path (utf8app);
588                 g_free (utf8appmemory);
589                 return TRUE;
590         }
591
592         if (g_file_test (utf8app, G_FILE_TEST_IS_EXECUTABLE) && !g_file_test (utf8app, G_FILE_TEST_IS_DIR)) {
593                 *completed = quote_path (utf8app);
594                 g_free (utf8appmemory);
595                 return TRUE;
596         }
597         
598         found = g_find_program_in_path (utf8app);
599         if (found == NULL) {
600                 *completed = NULL;
601                 g_free (utf8appmemory);
602                 return FALSE;
603         }
604
605         *completed = quote_path (found);
606         g_free (found);
607         g_free (utf8appmemory);
608         return TRUE;
609 }
610
611 MonoBoolean ves_icall_System_Diagnostics_Process_ShellExecuteEx_internal (MonoProcessStartInfo *proc_start_info, MonoProcInfo *process_info)
612 {
613         SHELLEXECUTEINFO shellex = {0};
614         gboolean ret;
615
616         shellex.cbSize = sizeof(SHELLEXECUTEINFO);
617         shellex.fMask = SEE_MASK_FLAG_DDEWAIT | SEE_MASK_NOCLOSEPROCESS | SEE_MASK_UNICODE;
618         shellex.nShow = proc_start_info->window_style;
619         shellex.nShow = (shellex.nShow == 0) ? 1 : (shellex.nShow == 1 ? 0 : shellex.nShow);
620         
621         
622         if (proc_start_info->filename != NULL) {
623                 shellex.lpFile = mono_string_chars (proc_start_info->filename);
624         }
625
626         if (proc_start_info->arguments != NULL) {
627                 shellex.lpParameters = mono_string_chars (proc_start_info->arguments);
628         }
629
630         if (proc_start_info->verb != NULL &&
631             mono_string_length (proc_start_info->verb) != 0) {
632                 shellex.lpVerb = mono_string_chars (proc_start_info->verb);
633         }
634
635         if (proc_start_info->working_directory != NULL &&
636             mono_string_length (proc_start_info->working_directory) != 0) {
637                 shellex.lpDirectory = mono_string_chars (proc_start_info->working_directory);
638         }
639
640         if (proc_start_info->error_dialog) {    
641                 shellex.hwnd = proc_start_info->error_dialog_parent_handle;
642         } else {
643                 shellex.fMask |= SEE_MASK_FLAG_NO_UI;
644         }
645
646         ret = ShellExecuteEx (&shellex);
647         if (ret == FALSE) {
648                 process_info->pid = -GetLastError ();
649         } else {
650                 process_info->process_handle = shellex.hProcess;
651                 process_info->thread_handle = NULL;
652                 /* It appears that there's no way to get the pid from a
653                  * process handle before windows xp.  Really.
654                  */
655 #if defined(HAVE_GETPROCESSID) && !defined(MONO_CROSS_COMPILE)
656                 process_info->pid = GetProcessId (shellex.hProcess);
657 #else
658                 process_info->pid = 0;
659 #endif
660                 process_info->tid = 0;
661         }
662
663         return (ret);
664 }
665
666 MonoBoolean ves_icall_System_Diagnostics_Process_CreateProcess_internal (MonoProcessStartInfo *proc_start_info, HANDLE stdin_handle, HANDLE stdout_handle, HANDLE stderr_handle, MonoProcInfo *process_info)
667 {
668         gboolean ret;
669         gunichar2 *dir;
670         STARTUPINFO startinfo={0};
671         PROCESS_INFORMATION procinfo;
672         gunichar2 *shell_path = NULL;
673         gchar *env_vars = NULL;
674         gboolean free_shell_path = TRUE;
675         gchar *spath = NULL;
676         MonoString *cmd = proc_start_info->arguments;
677         guint32 creation_flags, logon_flags;
678         
679         startinfo.cb=sizeof(STARTUPINFO);
680         startinfo.dwFlags=STARTF_USESTDHANDLES;
681         startinfo.hStdInput=stdin_handle;
682         startinfo.hStdOutput=stdout_handle;
683         startinfo.hStdError=stderr_handle;
684
685         creation_flags = CREATE_UNICODE_ENVIRONMENT;
686         if (proc_start_info->create_no_window)
687                 creation_flags |= CREATE_NO_WINDOW;
688         
689         shell_path = mono_string_chars (proc_start_info->filename);
690         complete_path (shell_path, &spath);
691         if (spath == NULL) {
692                 process_info->pid = -ERROR_FILE_NOT_FOUND;
693                 return FALSE;
694         }
695 #ifdef TARGET_WIN32
696         /* Seems like our CreateProcess does not work as the windows one.
697          * This hack is needed to deal with paths containing spaces */
698         shell_path = NULL;
699         free_shell_path = FALSE;
700         if (cmd) {
701                 gchar *newcmd, *tmp;
702                 tmp = mono_string_to_utf8 (cmd);
703                 newcmd = g_strdup_printf ("%s %s", spath, tmp);
704                 cmd = mono_string_new_wrapper (newcmd);
705                 g_free (tmp);
706                 g_free (newcmd);
707         }
708         else {
709                 cmd = mono_string_new_wrapper (spath);
710         }
711 #else
712         shell_path = g_utf8_to_utf16 (spath, -1, NULL, NULL, NULL);
713 #endif
714         g_free (spath);
715
716         if (process_info->env_keys != NULL) {
717                 gint i, len; 
718                 MonoString *ms;
719                 MonoString *key, *value;
720                 gunichar2 *str, *ptr;
721                 gunichar2 *equals16;
722
723                 for (len = 0, i = 0; i < mono_array_length (process_info->env_keys); i++) {
724                         ms = mono_array_get (process_info->env_values, MonoString *, i);
725                         if (ms == NULL)
726                                 continue;
727
728                         len += mono_string_length (ms) * sizeof (gunichar2);
729                         ms = mono_array_get (process_info->env_keys, MonoString *, i);
730                         len += mono_string_length (ms) * sizeof (gunichar2);
731                         len += 2 * sizeof (gunichar2);
732                 }
733
734                 equals16 = g_utf8_to_utf16 ("=", 1, NULL, NULL, NULL);
735                 ptr = str = g_new0 (gunichar2, len + 1);
736                 for (i = 0; i < mono_array_length (process_info->env_keys); i++) {
737                         value = mono_array_get (process_info->env_values, MonoString *, i);
738                         if (value == NULL)
739                                 continue;
740
741                         key = mono_array_get (process_info->env_keys, MonoString *, i);
742                         memcpy (ptr, mono_string_chars (key), mono_string_length (key) * sizeof (gunichar2));
743                         ptr += mono_string_length (key);
744
745                         memcpy (ptr, equals16, sizeof (gunichar2));
746                         ptr++;
747
748                         memcpy (ptr, mono_string_chars (value), mono_string_length (value) * sizeof (gunichar2));
749                         ptr += mono_string_length (value);
750                         ptr++;
751                 }
752
753                 g_free (equals16);
754                 env_vars = (gchar *) str;
755         }
756         
757         /* The default dir name is "".  Turn that into NULL to mean
758          * "current directory"
759          */
760         if(proc_start_info->working_directory == NULL || mono_string_length (proc_start_info->working_directory)==0) {
761                 dir=NULL;
762         } else {
763                 dir=mono_string_chars (proc_start_info->working_directory);
764         }
765
766         if (process_info->username) {
767                 logon_flags = process_info->load_user_profile ? LOGON_WITH_PROFILE : 0;
768                 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);
769         } else {
770                 ret=CreateProcess (shell_path, cmd? mono_string_chars (cmd): NULL, NULL, NULL, TRUE, creation_flags, env_vars, dir, &startinfo, &procinfo);
771         }
772
773         g_free (env_vars);
774         if (free_shell_path)
775                 g_free (shell_path);
776
777         if(ret) {
778                 process_info->process_handle=procinfo.hProcess;
779                 /*process_info->thread_handle=procinfo.hThread;*/
780                 process_info->thread_handle=NULL;
781                 if (procinfo.hThread != NULL && procinfo.hThread != INVALID_HANDLE_VALUE)
782                         CloseHandle(procinfo.hThread);
783                 process_info->pid=procinfo.dwProcessId;
784                 process_info->tid=procinfo.dwThreadId;
785         } else {
786                 process_info->pid = -GetLastError ();
787         }
788         
789         return(ret);
790 }
791
792 MonoBoolean ves_icall_System_Diagnostics_Process_WaitForExit_internal (MonoObject *this, HANDLE process, gint32 ms)
793 {
794         guint32 ret;
795         
796         MONO_PREPARE_BLOCKING
797         if(ms<0) {
798                 /* Wait forever */
799                 ret=WaitForSingleObjectEx (process, INFINITE, TRUE);
800         } else {
801                 ret=WaitForSingleObjectEx (process, ms, TRUE);
802         }
803         MONO_FINISH_BLOCKING
804
805         if(ret==WAIT_OBJECT_0) {
806                 return(TRUE);
807         } else {
808                 return(FALSE);
809         }
810 }
811
812 MonoBoolean ves_icall_System_Diagnostics_Process_WaitForInputIdle_internal (MonoObject *this, HANDLE process, gint32 ms)
813 {
814         guint32 ret;
815         
816         if(ms<0) {
817                 /* Wait forever */
818                 ret=WaitForInputIdle (process, INFINITE);
819         } else {
820                 ret=WaitForInputIdle (process, ms);
821         }
822
823         return (ret) ? FALSE : TRUE;
824 }
825
826 static guint64
827 file_time_to_guint64 (FILETIME *time)
828 {
829         return ((guint64)time->dwHighDateTime << 32) | ((guint64)time->dwLowDateTime);
830 }
831
832 gint64 ves_icall_System_Diagnostics_Process_ExitTime_internal (HANDLE process)
833 {
834         gboolean ret;
835         FILETIME create_time, exit_time, kernel_time, user_time;
836         
837         ret = GetProcessTimes (process, &create_time, &exit_time, &kernel_time,
838                                                    &user_time);
839         if (ret)
840                 return file_time_to_guint64 (&exit_time);
841         else
842                 return 0;
843 }
844
845 gint64 ves_icall_System_Diagnostics_Process_StartTime_internal (HANDLE process)
846 {
847         gboolean ret;
848         FILETIME create_time, exit_time, kernel_time, user_time;
849         
850         ret = GetProcessTimes (process, &create_time, &exit_time, &kernel_time,
851                                                    &user_time);
852         if (ret)
853                 return file_time_to_guint64 (&create_time);
854         else
855                 return 0;
856 }
857
858 gint32 ves_icall_System_Diagnostics_Process_ExitCode_internal (HANDLE process)
859 {
860         DWORD code;
861         
862         GetExitCodeProcess (process, &code);
863         
864         LOGDEBUG (g_message ("%s: process exit code is %d", __func__, code));
865         
866         return(code);
867 }
868
869 MonoString *ves_icall_System_Diagnostics_Process_ProcessName_internal (HANDLE process)
870 {
871         MonoString *string;
872         gboolean ok;
873         HMODULE mod;
874         gunichar2 name[MAX_PATH];
875         DWORD needed;
876         guint32 len;
877         
878         ok=EnumProcessModules (process, &mod, sizeof(mod), &needed);
879         if(ok==FALSE) {
880                 return(NULL);
881         }
882         
883         len=GetModuleBaseName (process, mod, name, MAX_PATH);
884         if(len==0) {
885                 return(NULL);
886         }
887         
888         LOGDEBUG (g_message ("%s: process name is [%s]", __func__, g_utf16_to_utf8 (name, -1, NULL, NULL, NULL)));
889         
890         string=mono_string_new_utf16 (mono_domain_get (), name, len);
891         
892         return(string);
893 }
894
895 /* Returns an array of pids */
896 MonoArray *
897 ves_icall_System_Diagnostics_Process_GetProcesses_internal (void)
898 {
899 #if !defined(HOST_WIN32)
900         MonoArray *procs;
901         gpointer *pidarray;
902         int i, count;
903
904         pidarray = mono_process_list (&count);
905         if (!pidarray) {
906                 mono_set_pending_exception (mono_get_exception_not_supported ("This system does not support EnumProcesses"));
907                 return NULL;
908         }
909         procs = mono_array_new (mono_domain_get (), mono_get_int32_class (), count);
910         if (sizeof (guint32) == sizeof (gpointer)) {
911                 memcpy (mono_array_addr (procs, guint32, 0), pidarray, count * sizeof (gint32));
912         } else {
913                 for (i = 0; i < count; ++i)
914                         *(mono_array_addr (procs, guint32, i)) = GPOINTER_TO_UINT (pidarray [i]);
915         }
916         g_free (pidarray);
917
918         return procs;
919 #else
920         MonoArray *procs;
921         gboolean ret;
922         DWORD needed;
923         int count;
924         guint32 *pids;
925
926         count = 512;
927         do {
928                 pids = g_new0 (guint32, count);
929                 ret = EnumProcesses (pids, count * sizeof (guint32), &needed);
930                 if (ret == FALSE) {
931                         MonoException *exc;
932
933                         g_free (pids);
934                         pids = NULL;
935                         exc = mono_get_exception_not_supported ("This system does not support EnumProcesses");
936                         mono_set_pending_exception (exc);
937                         return NULL;
938                 }
939                 if (needed < (count * sizeof (guint32)))
940                         break;
941                 g_free (pids);
942                 pids = NULL;
943                 count = (count * 3) / 2;
944         } while (TRUE);
945
946         count = needed / sizeof (guint32);
947         procs = mono_array_new (mono_domain_get (), mono_get_int32_class (), count);
948         memcpy (mono_array_addr (procs, guint32, 0), pids, needed);
949         g_free (pids);
950         pids = NULL;
951         
952         return procs;
953 #endif
954 }
955
956 MonoBoolean ves_icall_System_Diagnostics_Process_GetWorkingSet_internal (HANDLE process, guint32 *min, guint32 *max)
957 {
958         gboolean ret;
959         SIZE_T ws_min, ws_max;
960         
961         ret=GetProcessWorkingSetSize (process, &ws_min, &ws_max);
962         *min=(guint32)ws_min;
963         *max=(guint32)ws_max;
964         
965         return(ret);
966 }
967
968 MonoBoolean ves_icall_System_Diagnostics_Process_SetWorkingSet_internal (HANDLE process, guint32 min, guint32 max, MonoBoolean use_min)
969 {
970         gboolean ret;
971         SIZE_T ws_min;
972         SIZE_T ws_max;
973         
974         ret=GetProcessWorkingSetSize (process, &ws_min, &ws_max);
975         if(ret==FALSE) {
976                 return(FALSE);
977         }
978         
979         if(use_min==TRUE) {
980                 ws_min=(SIZE_T)min;
981         } else {
982                 ws_max=(SIZE_T)max;
983         }
984         
985         ret=SetProcessWorkingSetSize (process, ws_min, ws_max);
986
987         return(ret);
988 }
989
990 MonoBoolean
991 ves_icall_System_Diagnostics_Process_Kill_internal (HANDLE process, gint32 sig)
992 {
993         /* sig == 1 -> Kill, sig == 2 -> CloseMainWindow */
994
995         return TerminateProcess (process, -sig);
996 }
997
998 gint64
999 ves_icall_System_Diagnostics_Process_Times (HANDLE process, gint32 type)
1000 {
1001         FILETIME create_time, exit_time, kernel_time, user_time;
1002         
1003         if (GetProcessTimes (process, &create_time, &exit_time, &kernel_time, &user_time)) {
1004                 guint64 ktime = file_time_to_guint64 (&kernel_time);
1005                 guint64 utime = file_time_to_guint64 (&user_time);
1006
1007                 if (type == 0)
1008                         return utime;
1009                 else if (type == 1)
1010                         return ktime;
1011                 else
1012                         return ktime + utime;
1013         }
1014         return 0;
1015 }
1016
1017 gint32
1018 ves_icall_System_Diagnostics_Process_GetPriorityClass (HANDLE process, gint32 *error)
1019 {
1020         gint32 ret = GetPriorityClass (process);
1021         *error = ret == 0 ? GetLastError () : 0;
1022         return ret;
1023 }
1024
1025 MonoBoolean
1026 ves_icall_System_Diagnostics_Process_SetPriorityClass (HANDLE process, gint32 priority_class, gint32 *error)
1027 {
1028         gboolean ret = SetPriorityClass (process, priority_class);
1029         *error = ret == 0 ? GetLastError () : 0;
1030         return ret;
1031 }
1032
1033 HANDLE
1034 ves_icall_System_Diagnostics_Process_ProcessHandle_duplicate (HANDLE process)
1035 {
1036         HANDLE ret;
1037
1038         LOGDEBUG (g_message ("%s: Duplicating process handle %p", __func__, process));
1039         
1040         DuplicateHandle (GetCurrentProcess (), process, GetCurrentProcess (),
1041                          &ret, THREAD_ALL_ACCESS, TRUE, 0);
1042         
1043         return ret;
1044 }
1045
1046 void
1047 ves_icall_System_Diagnostics_Process_ProcessHandle_close (HANDLE process)
1048 {
1049         LOGDEBUG (g_message ("%s: Closing process handle %p", __func__, process));
1050
1051         CloseHandle (process);
1052 }
1053
1054 gint64
1055 ves_icall_System_Diagnostics_Process_GetProcessData (int pid, gint32 data_type, gint32 *error)
1056 {
1057         MonoProcessError perror;
1058         guint64 res;
1059
1060         res = mono_process_get_data_with_error (GINT_TO_POINTER (pid), data_type, &perror);
1061         if (error)
1062                 *error = perror;
1063         return res;
1064 }
1065