7d8b921cc43eb09e17721354a62dd9c0ad7da7ee
[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  */
9
10 #include <config.h>
11
12 #include <glib.h>
13 #include <string.h>
14
15 #include <mono/metadata/object.h>
16 #include <mono/metadata/process.h>
17 #include <mono/metadata/assembly.h>
18 #include <mono/metadata/appdomain.h>
19 #include <mono/metadata/image.h>
20 #include <mono/metadata/cil-coff.h>
21 #include <mono/metadata/exception.h>
22 #include <mono/utils/strenc.h>
23 #include <mono/io-layer/io-layer.h>
24 /* FIXME: fix this code to not depend so much on the inetrnals */
25 #include <mono/metadata/class-internals.h>
26
27 #undef DEBUG
28
29 HANDLE ves_icall_System_Diagnostics_Process_GetProcess_internal (guint32 pid)
30 {
31         HANDLE handle;
32         
33         MONO_ARCH_SAVE_REGS;
34
35         /* GetCurrentProcess returns a pseudo-handle, so use
36          * OpenProcess instead
37          */
38         handle=OpenProcess (PROCESS_ALL_ACCESS, TRUE, pid);
39         
40         if(handle==NULL) {
41                 /* FIXME: Throw an exception */
42                 return(NULL);
43         }
44         
45         return(handle);
46 }
47
48 guint32 ves_icall_System_Diagnostics_Process_GetPid_internal (void)
49 {
50         MONO_ARCH_SAVE_REGS;
51
52         return(GetCurrentProcessId ());
53 }
54
55 void ves_icall_System_Diagnostics_Process_Process_free_internal (MonoObject *this,
56                                                                  HANDLE process)
57 {
58         MONO_ARCH_SAVE_REGS;
59
60 #ifdef THREAD_DEBUG
61         g_message (G_GNUC_PRETTY_FUNCTION ": Closing process %p, handle %p",
62                    this, process);
63 #endif
64
65         CloseHandle (process);
66 }
67
68 #define STASH_SYS_ASS(this) \
69         if(system_assembly == NULL) { \
70                 system_assembly=this->vtable->klass->image; \
71         }
72
73 static MonoImage *system_assembly=NULL;
74
75 static guint32 unicode_chars (const gunichar2 *str)
76 {
77         guint32 len=0;
78         
79         do {
80                 if(str[len]=='\0') {
81                         return(len);
82                 }
83                 len++;
84         } while(1);
85 }
86
87 static guint32 unicode_bytes (const gunichar2 *str)
88 {
89         guint32 len=0;
90         
91         do {
92                 if(str[len]=='\0') {
93                         /* Include the terminators */
94                         return((len*2)+2);
95                 }
96                 len++;
97         } while(1);
98 }
99
100 /* 
101  * compare a null-terminated utf16 string and a normal string.
102  * Can be used only for ascii or latin1 chars.
103  */
104 static gboolean
105 unicode_string_equals (const gunichar2 *str1, const guchar *str2)
106 {
107         while (*str1 && *str2) {
108                 if (*str1 != *str2)
109                         return FALSE;
110                 ++str1;
111                 ++str2;
112         }
113         return *str1 == *str2;
114 }
115
116 static void process_set_field_object (MonoObject *obj, const guchar *fieldname,
117                                       MonoObject *data)
118 {
119         MonoClassField *field;
120
121 #ifdef DEBUG
122         g_message (G_GNUC_PRETTY_FUNCTION ": Setting field %s to object at %p",
123                    fieldname, data);
124 #endif
125
126         field=mono_class_get_field_from_name (mono_object_class (obj),
127                                               fieldname);
128         *(MonoObject **)(((char *)obj) + field->offset)=data;
129 }
130
131 static void process_set_field_string (MonoObject *obj, const guchar *fieldname,
132                                       const gunichar2 *val, guint32 len)
133 {
134         MonoClassField *field;
135         MonoString *string;
136
137 #ifdef DEBUG
138         g_message (G_GNUC_PRETTY_FUNCTION ": Setting field %s to [%s]",
139                    fieldname, g_utf16_to_utf8 (val, len, NULL, NULL, NULL));
140 #endif
141
142         string=mono_string_new_utf16 (mono_object_domain (obj), val, len);
143         
144         field=mono_class_get_field_from_name (mono_object_class (obj),
145                                               fieldname);
146         *(MonoString **)(((char *)obj) + field->offset)=string;
147 }
148
149 static void process_set_field_string_utf8 (MonoObject *obj,
150                                            const guchar *fieldname,
151                                            const guchar *val)
152 {
153         MonoClassField *field;
154         MonoString *string;
155
156 #ifdef DEBUG
157         g_message (G_GNUC_PRETTY_FUNCTION ": Setting field %s to [%s]",
158                    fieldname, val);
159 #endif
160
161         string=mono_string_new (mono_object_domain (obj), val);
162         
163         field=mono_class_get_field_from_name (mono_object_class (obj),
164                                               fieldname);
165         *(MonoString **)(((char *)obj) + field->offset)=string;
166 }
167
168 static void process_set_field_int (MonoObject *obj, const guchar *fieldname,
169                                    guint32 val)
170 {
171         MonoClassField *field;
172
173 #ifdef DEBUG
174         g_message (G_GNUC_PRETTY_FUNCTION ": Setting field %s to %d",
175                    fieldname, val);
176 #endif
177         
178         field=mono_class_get_field_from_name (mono_object_class (obj),
179                                               fieldname);
180         *(guint32 *)(((char *)obj) + field->offset)=val;
181 }
182
183 static void process_set_field_bool (MonoObject *obj, const guchar *fieldname,
184                                     gboolean val)
185 {
186         MonoClassField *field;
187
188 #ifdef DEBUG
189         g_message (G_GNUC_PRETTY_FUNCTION ": Setting field %s to %s",
190                    fieldname, val?"TRUE":"FALSE");
191 #endif
192         
193         field=mono_class_get_field_from_name (mono_object_class (obj),
194                                               fieldname);
195         *(guint8 *)(((char *)obj) + field->offset)=val;
196 }
197
198 typedef struct {
199         guint16 data_len;
200         guint16 value_len;
201         guint16 type;
202         gunichar2 *key;
203 } version_data;
204
205 /* Returns a pointer to the value data, because theres no way to know
206  * how big that data is (value_len is set to zero for most blocks :-()
207  */
208 static gpointer process_get_versioninfo_block (gpointer data,
209                                                version_data *block)
210 {
211         block->data_len=*((guint16 *)data);
212         data = (char *)data + sizeof(guint16);
213         block->value_len=*((guint16 *)data);
214         data = (char *)data + sizeof(guint16);
215
216         /* No idea what the type is supposed to indicate */
217         block->type=*((guint16 *)data);
218         data = (char *)data + sizeof(guint16);
219         block->key=((gunichar2 *)data);
220
221         /* skip over the key (including the terminator) */
222         data=((gunichar2 *)data)+(unicode_chars (block->key)+1);
223
224         /* align on a 32-bit boundary */
225         data=(gpointer)((char *)data + 3);
226         data=(gpointer)((char *)data - (GPOINTER_TO_INT(data) & 3));
227         
228         return(data);
229 }
230
231 /* Returns a pointer to the byte following the Var block */
232 static gpointer process_read_var_block (MonoObject *filever, gpointer data_ptr,
233                                         guint16 data_len)
234 {
235         /* Not currently interested in the VarFileInfo block.  This
236          * might change if language support is needed for file version
237          * strings (VarFileInfo contains lists of supported
238          * languages.)
239          */
240         version_data block;
241
242         /* data_ptr is pointing at a Var block of length data_len */
243         data_ptr=process_get_versioninfo_block (data_ptr, &block);
244         data_ptr=((guchar *)data_ptr)+block.value_len;
245
246         return(data_ptr);
247 }
248
249 /* Returns a pointer to the byte following the String block, or NULL
250  * if the data read hits padding.  We can't recover from this because
251  * the data length does not include padding bytes, so it's not
252  * possible to just return the start position + length.
253  */
254 static gpointer process_read_string_block (MonoObject *filever,
255                                            gpointer data_ptr,
256                                            guint16 data_len,
257                                            gboolean store)
258 {
259         version_data block;
260         guint16 string_len=28; /* Length of the StringTable block */
261
262         /* data_ptr is pointing at an array of one or more String
263          * blocks with total length (not including alignment padding)
264          * of data_len.
265          */
266         while(string_len<data_len) {
267                 gunichar2 *value;
268                 
269                 /* align on a 32-bit boundary */
270                 data_ptr=(gpointer)((char *)data_ptr + 3);
271                 data_ptr=(gpointer)((char *)data_ptr -
272                     (GPOINTER_TO_INT(data_ptr) & 3));
273
274                 data_ptr=process_get_versioninfo_block (data_ptr, &block);
275                 if(block.data_len==0) {
276                         /* We must have hit padding, so give up
277                          * processing now
278                          */
279 #ifdef DEBUG
280                         g_message (G_GNUC_PRETTY_FUNCTION
281                                    ": Hit 0-length block, giving up");
282 #endif
283                         return(NULL);
284                 }
285                 
286                 string_len=string_len+block.data_len;
287                 value=(gunichar2 *)data_ptr;
288                 /* Skip over the value */
289                 data_ptr=((gunichar2 *)data_ptr)+block.value_len;
290
291                 if(store==TRUE) {
292                         if (unicode_string_equals (block.key, "Comments")) {
293                                 process_set_field_string (filever, "comments", value, unicode_chars (value));
294                         } else if (unicode_string_equals (block.key, "CompanyName")) {
295                                 process_set_field_string (filever, "companyname", value, unicode_chars (value));
296                         } else if (unicode_string_equals (block.key, "FileDescription")) {
297                                 process_set_field_string (filever, "filedescription", value, unicode_chars (value));
298                         } else if (unicode_string_equals (block.key, "FileVersion")) {
299                                 process_set_field_string (filever, "fileversion", value, unicode_chars (value));
300                         } else if (unicode_string_equals (block.key, "InternalName")) {
301                                 process_set_field_string (filever, "internalname", value, unicode_chars (value));
302                         } else if (unicode_string_equals (block.key, "LegalCopyright")) {
303                                 process_set_field_string (filever, "legalcopyright", value, unicode_chars (value));
304                         } else if (unicode_string_equals (block.key, "LegalTrademarks")) {
305                                 process_set_field_string (filever, "legaltrademarks", value, unicode_chars (value));
306                         } else if (unicode_string_equals (block.key, "OriginalFilename")) {
307                                 process_set_field_string (filever, "originalfilename", value, unicode_chars (value));
308                         } else if (unicode_string_equals (block.key, "PrivateBuild")) {
309                                 process_set_field_string (filever, "privatebuild", value, unicode_chars (value));
310                         } else if (unicode_string_equals (block.key, "ProductName")) {
311                                 process_set_field_string (filever, "productname", value, unicode_chars (value));
312                         } else if (unicode_string_equals (block.key, "ProductVersion")) {
313                                 process_set_field_string (filever, "productversion", value, unicode_chars (value));
314                         } else if (unicode_string_equals (block.key, "SpecialBuild")) {
315                                 process_set_field_string (filever, "specialbuild", value, unicode_chars (value));
316                         } else {
317                                 /* Not an error, just not interesting
318                                  * in this case
319                                  */
320                         }
321                 }
322         }
323         
324         return(data_ptr);
325 }
326
327 /* returns a pointer to the byte following the Stringtable block, or
328  * NULL if the data read hits padding.  We can't recover from this
329  * because the data length does not include padding bytes, so it's not
330  * possible to just return the start position + length
331  */
332 static gpointer process_read_stringtable_block (MonoObject *filever,
333                                                 gpointer data_ptr,
334                                                 guint16 data_len)
335 {
336         version_data block;
337         gchar *language;
338         guint16 string_len=36;  /* length of the StringFileInfo block */
339
340         /* data_ptr is pointing at an array of StringTable blocks,
341          * with total length (not including alignment padding) of
342          * data_len.
343          */
344
345         while(string_len<data_len) {
346                 /* align on a 32-bit boundary */
347                 data_ptr=(gpointer)((char *)data_ptr + 3);
348                 data_ptr=(gpointer)((char *)data_ptr -
349                     (GPOINTER_TO_INT(data_ptr) & 3));
350
351                 data_ptr=process_get_versioninfo_block (data_ptr, &block);
352                 if(block.data_len==0) {
353                         /* We must have hit padding, so give up
354                          * processing now
355                          */
356 #ifdef DEBUG
357                         g_message (G_GNUC_PRETTY_FUNCTION
358                                    ": Hit 0-length block, giving up");
359 #endif
360                         return(NULL);
361                 }
362                 string_len=string_len+block.data_len;
363
364                 language = g_utf16_to_utf8 (block.key, unicode_bytes (block.key), NULL, NULL, NULL);
365                 g_strdown (language);
366
367                 /* Kludge: treat en_US as neutral too */
368                 if (!strcmp (language, "007f04b0") ||
369                     !strcmp (language, "000004b0") ||
370                     !strcmp (language, "040904b0")) {
371                         /* Got the one we're interested in */
372                         process_set_field_string_utf8 (filever, "language",
373                                                        "Language Neutral");
374                         
375                         data_ptr=process_read_string_block (filever, data_ptr,
376                                                             block.data_len,
377                                                             TRUE);
378                 } else {
379                         /* Some other language.  We might want to do
380                          * something with this in the future.
381                          */
382                         data_ptr=process_read_string_block (filever, data_ptr,
383                                                             block.data_len,
384                                                             FALSE);
385                 }
386                 g_free (language);
387
388                 if(data_ptr==NULL) {
389                         /* Child block hit padding */
390 #ifdef DEBUG
391                         g_message (G_GNUC_PRETTY_FUNCTION ": Child block hit 0-length block, giving up");
392 #endif
393                         return(NULL);
394                 }
395         }
396                 
397         return(data_ptr);
398 }
399
400 static void process_read_fixedfileinfo_block (MonoObject *filever,
401                                               VS_FIXEDFILEINFO *ffi)
402 {
403 #ifdef DEBUG
404         g_message (G_GNUC_PRETTY_FUNCTION ": ffi: sig 0x%x, strucver 0x%x, fileverm 0x%x, fileverl 0x%x, prodverm 0x%x, prodverl 0x%x, ffmask 0x%x, ff 0x%x, os 0x%x, type 0x%x, subtype 0x%x, datem 0x%x, datel 0x%x", ffi->dwSignature, ffi->dwStrucVersion, ffi->dwFileVersionMS, ffi->dwFileVersionLS, ffi->dwProductVersionMS, ffi->dwProductVersionLS, ffi->dwFileFlagsMask, ffi->dwFileFlags, ffi->dwFileOS, ffi->dwFileType, ffi->dwFileSubtype, ffi->dwFileDateMS, ffi->dwFileDateLS);
405 #endif
406                 
407         process_set_field_int (filever, "filemajorpart",
408                                HIWORD (ffi->dwFileVersionMS));
409         process_set_field_int (filever, "fileminorpart",
410                                LOWORD (ffi->dwFileVersionMS));
411         process_set_field_int (filever, "filebuildpart",
412                                HIWORD (ffi->dwFileVersionLS));
413         process_set_field_int (filever, "fileprivatepart",
414                                LOWORD (ffi->dwFileVersionLS));
415                 
416         process_set_field_int (filever, "productmajorpart",
417                                HIWORD (ffi->dwProductVersionMS));
418         process_set_field_int (filever, "productminorpart",
419                                LOWORD (ffi->dwProductVersionMS));
420         process_set_field_int (filever, "productbuildpart",
421                                HIWORD (ffi->dwProductVersionLS));
422         process_set_field_int (filever, "productprivatepart",
423                                LOWORD (ffi->dwProductVersionLS));
424         
425         process_set_field_bool (filever, "isdebug",
426                                 ffi->dwFileFlags&VS_FF_DEBUG);
427         process_set_field_bool (filever, "isprerelease",
428                                 ffi->dwFileFlags&VS_FF_PRERELEASE);
429         process_set_field_bool (filever, "ispatched",
430                                 ffi->dwFileFlags&VS_FF_PATCHED);
431         process_set_field_bool (filever, "isprivatebuild",
432                                 ffi->dwFileFlags&VS_FF_PRIVATEBUILD);
433         process_set_field_bool (filever, "isspecialbuild",
434                                 ffi->dwFileFlags&VS_FF_SPECIALBUILD);
435 }
436
437 static void process_get_fileversion (MonoObject *filever, MonoImage *image)
438 {
439         MonoPEResourceDataEntry *version_info;
440         gpointer data;
441         VS_FIXEDFILEINFO *ffi;
442         gpointer data_ptr;
443         version_data block;
444         gint32 data_len; /* signed to guard against underflow */
445
446         version_info=mono_image_lookup_resource (image,
447                                                  MONO_PE_RESOURCE_ID_VERSION,
448                                                  0, NULL);
449 #ifdef DEBUG
450         g_message (G_GNUC_PRETTY_FUNCTION ": image_lookup returned %p",
451                    version_info);
452 #endif
453
454         if(version_info==NULL) {
455                 return;
456         }
457         
458         data=mono_image_rva_map (image,
459                                version_info->rde_data_offset);
460         if(data==NULL) {
461                 return;
462         }
463
464         /* See io-layer/versioninfo.h for the gory details on how this
465          * data is laid out. (data should be pointing to
466          * VS_VERSIONINFO data).
467          */
468
469         data_ptr=process_get_versioninfo_block (data, &block);
470                 
471         data_len=block.data_len;
472                 
473         if(block.value_len!=sizeof(VS_FIXEDFILEINFO)) {
474 #ifdef DEBUG
475                 g_message (G_GNUC_PRETTY_FUNCTION
476                            ": FIXEDFILEINFO size mismatch");
477 #endif
478                 return;
479         }
480
481         if (!unicode_string_equals (block.key, "VS_VERSION_INFO")) {
482 #ifdef DEBUG
483                 g_message (G_GNUC_PRETTY_FUNCTION
484                            ": VS_VERSION_INFO mismatch");
485 #endif
486                 return;
487         }
488
489         ffi=((VS_FIXEDFILEINFO *)data_ptr);
490         data_ptr = (char *)data_ptr + sizeof(VS_FIXEDFILEINFO);
491         if((ffi->dwSignature!=VS_FFI_SIGNATURE) ||
492            (ffi->dwStrucVersion!=VS_FFI_STRUCVERSION)) {
493 #ifdef DEBUG
494                 g_message (G_GNUC_PRETTY_FUNCTION
495                            ": FIXEDFILEINFO bad signature");
496 #endif
497                 return;
498         }
499         process_read_fixedfileinfo_block (filever, ffi);
500         
501         /* Subtract the 92 bytes we've already seen */
502         data_len -= 92;
503         
504         /* There now follow zero or one StringFileInfo blocks and zero
505          * or one VarFileInfo blocks
506          */
507         while(data_len > 0) {
508                 /* align on a 32-bit boundary */
509                 data_ptr=(gpointer)((char *)data_ptr + 3);
510                 data_ptr=(gpointer)((char *)data_ptr -
511                     (GPOINTER_TO_INT(data_ptr) & 3));
512
513                 data_ptr=process_get_versioninfo_block (data_ptr, &block);
514                 if(block.data_len==0) {
515                         /* We must have hit padding, so give up
516                          * processing now
517                          */
518 #ifdef DEBUG
519                         g_message (G_GNUC_PRETTY_FUNCTION
520                                    ": Hit 0-length block, giving up");
521 #endif
522                         return;
523                 }
524                 
525                 data_len=data_len-block.data_len;
526
527                 if (unicode_string_equals (block.key, "VarFileInfo")) {
528                         data_ptr=process_read_var_block (filever, data_ptr,
529                                                          block.data_len);
530                 } else if (unicode_string_equals (block.key, "StringFileInfo")) {
531                         data_ptr=process_read_stringtable_block (filever, data_ptr, block.data_len);
532                 } else {
533                         /* Bogus data */
534 #ifdef DEBUG
535                         g_message (G_GNUC_PRETTY_FUNCTION
536                                    ": Not a valid VERSIONINFO child block");
537                         return;
538 #endif
539                 }
540
541                 if(data_ptr==NULL) {
542                         /* Child block hit padding */
543 #ifdef DEBUG
544                         g_message (G_GNUC_PRETTY_FUNCTION ": Child block hit 0-length block, giving up");
545 #endif
546                         return;
547                 }
548         }
549 }
550
551 static void process_add_module (GPtrArray *modules, MonoAssembly *ass)
552 {
553         MonoClass *proc_class, *filever_class;
554         MonoObject *item, *filever;
555         MonoDomain *domain=mono_domain_get ();
556         gchar *modulename;
557         const char* filename;
558         
559         /* Build a System.Diagnostics.ProcessModule with the data.
560          * Leave BaseAddress and EntryPointAddress set to NULL,
561          * FileName is ass->image->name, FileVersionInfo is an object
562          * constructed from the PE image data referenced by
563          * ass->image, ModuleMemorySize set to 0, ModuleName the last
564          * component of FileName.
565          */
566         proc_class=mono_class_from_name (system_assembly, "System.Diagnostics",
567                                          "ProcessModule");
568         item=mono_object_new (domain, proc_class);
569
570         filever_class=mono_class_from_name (system_assembly,
571                                             "System.Diagnostics",
572                                             "FileVersionInfo");
573         filever=mono_object_new (domain, filever_class);
574         
575 #ifdef DEBUG
576         g_message (G_GNUC_PRETTY_FUNCTION ": recording assembly: FileName [%s] FileVersionInfo [%d.%d.%d.%d], ModuleName [%s]", ass->image->name, ass->aname.major, ass->aname.minor, ass->aname.build, ass->aname.revision, ass->image->name);
577 #endif
578
579         process_get_fileversion (filever, mono_assembly_get_image (ass));
580
581         filename = mono_image_get_filename (mono_assembly_get_image (ass));
582         process_set_field_string_utf8 (filever, "filename", filename);
583         process_set_field_string_utf8 (item, "filename", filename);
584         process_set_field_object (item, "version_info", filever);
585
586         modulename=g_path_get_basename (filename);
587         process_set_field_string_utf8 (item, "modulename", modulename);
588         g_free (modulename);
589
590         g_ptr_array_add (modules, item);
591 }
592
593 static void process_scan_modules (gpointer data, gpointer user_data)
594 {
595         MonoAssembly *ass=data;
596         GPtrArray *modules=user_data;
597
598         /* The main assembly is already in the list */
599         if(mono_assembly_get_main () != ass) {
600                 process_add_module (modules, ass);
601         }
602 }
603
604
605 /* Returns an array of System.Diagnostics.ProcessModule */
606 MonoArray *ves_icall_System_Diagnostics_Process_GetModules_internal (MonoObject *this)
607 {
608         /* I was going to use toolhelp for this, but then realised I
609          * was being an idiot :)
610          *
611          * (Toolhelp would give shared libraries open by the runtime,
612          * as well as open assemblies.  On windows my tests didnt find
613          * the assemblies loaded by mono either.)
614          */
615         GPtrArray *modules_list=g_ptr_array_new ();
616         MonoArray *arr;
617         guint32 i;
618         
619         MONO_ARCH_SAVE_REGS;
620
621         STASH_SYS_ASS (this);
622         
623         /* Make sure the first entry is the main module */
624         process_add_module (modules_list, mono_assembly_get_main ());
625         
626         mono_assembly_foreach (process_scan_modules, modules_list);
627
628         /* Build a MonoArray out of modules_list */
629         arr=mono_array_new (mono_domain_get (), mono_get_object_class (),
630                             modules_list->len);
631         
632         for(i=0; i<modules_list->len; i++) {
633                 mono_array_set (arr, MonoObject *, i,
634                                 g_ptr_array_index (modules_list, i));
635         }
636         
637         g_ptr_array_free (modules_list, FALSE);
638         
639         return(arr);
640 }
641
642 void ves_icall_System_Diagnostics_FileVersionInfo_GetVersionInfo_internal (MonoObject *this, MonoString *filename)
643 {
644         MonoImage *image;
645         guchar *filename_utf8;
646         
647         MONO_ARCH_SAVE_REGS;
648
649         STASH_SYS_ASS (this);
650         
651         filename_utf8 = mono_string_to_utf8 (filename);
652         image = mono_pe_file_open (filename_utf8, NULL);
653         g_free (filename_utf8);
654         
655         if(image==NULL) {
656                 /* FIXME: an exception might be appropriate here */
657 #ifdef DEBUG
658                 g_message (G_GNUC_PRETTY_FUNCTION ": Failed to load image");
659 #endif
660
661                 return;
662         }
663         
664         process_get_fileversion (this, image);
665         process_set_field_string_utf8 (this, "filename", mono_image_get_filename (image));
666         
667         mono_image_close (image);
668 }
669
670 /* Only used when UseShellExecute is false */
671 static gchar *
672 quote_path (const gchar *path)
673 {
674         gchar *res = g_shell_quote (path);
675 #ifdef PLATFORM_WIN32
676         {
677         gchar *q = res;
678         while (*q) {
679                 if (*q == '\'')
680                         *q = '\"';
681                 q++;
682         }
683         }
684 #endif
685         return res;
686 }
687
688 /* Only used when UseShellExecute is false */
689 static gboolean
690 complete_path (const gunichar2 *appname, gchar **completed)
691 {
692         gchar *utf8app;
693         gchar *found;
694
695         utf8app = g_utf16_to_utf8 (appname, -1, NULL, NULL, NULL);
696         if (g_path_is_absolute (utf8app)) {
697                 *completed = quote_path (utf8app);
698                 g_free (utf8app);
699                 return TRUE;
700         }
701
702         if (g_file_test (utf8app, G_FILE_TEST_IS_EXECUTABLE) && !g_file_test (utf8app, G_FILE_TEST_IS_DIR)) {
703                 *completed = quote_path (utf8app);
704                 g_free (utf8app);
705                 return TRUE;
706         }
707         
708         found = g_find_program_in_path (utf8app);
709         if (found == NULL) {
710                 *completed = NULL;
711                 g_free (utf8app);
712                 return FALSE;
713         }
714
715         *completed = quote_path (found);
716         g_free (found);
717         g_free (utf8app);
718         return TRUE;
719 }
720
721 MonoBoolean ves_icall_System_Diagnostics_Process_ShellExecuteEx_internal (MonoProcessStartInfo *proc_start_info, MonoProcInfo *process_info)
722 {
723         SHELLEXECUTEINFO shellex = {0};
724         gboolean ret;
725
726         shellex.cbSize = sizeof(SHELLEXECUTEINFO);
727         shellex.fMask = SEE_MASK_FLAG_DDEWAIT | SEE_MASK_NOCLOSEPROCESS | SEE_MASK_UNICODE;
728         shellex.lpFile = mono_string_chars (proc_start_info->filename);
729         shellex.lpParameters = mono_string_chars (proc_start_info->arguments);
730         shellex.nShow = SW_SHOWNORMAL;
731
732         if(mono_string_length (proc_start_info->verb)==0) {
733                 shellex.lpVerb = NULL;
734         } else {
735                 shellex.lpVerb = mono_string_chars (proc_start_info->verb);
736         }
737
738         if(mono_string_length (proc_start_info->working_directory)==0) {
739                 shellex.lpDirectory = NULL;
740         } else {
741                 shellex.lpDirectory = mono_string_chars (proc_start_info->working_directory);
742         }
743
744         ret = ShellExecuteEx (&shellex);
745         if (ret == FALSE) {
746                 process_info->pid = -GetLastError ();
747         } else {
748                 process_info->process_handle = shellex.hProcess;
749                 process_info->thread_handle = NULL;
750                 /* It appears that there's no way to get the pid from a
751                  * process handle before windows xp.  Really.
752                  */
753                 process_info->pid = 0;
754                 process_info->tid = 0;
755         }
756
757         return (ret);
758 }
759
760 MonoBoolean ves_icall_System_Diagnostics_Process_CreateProcess_internal (MonoProcessStartInfo *proc_start_info, HANDLE stdin_handle, HANDLE stdout_handle, HANDLE stderr_handle, MonoProcInfo *process_info)
761 {
762         gboolean ret;
763         gunichar2 *dir;
764         STARTUPINFO startinfo={0};
765         PROCESS_INFORMATION procinfo;
766         gunichar2 *shell_path = NULL;
767         gchar *env_vars = NULL;
768         gboolean free_shell_path = TRUE;
769 #ifdef PLATFORM_WIN32
770         gchar *newcmd, *tmp;
771 #endif
772         gchar *spath = NULL;
773         MonoString *cmd = proc_start_info->arguments;
774         
775         MONO_ARCH_SAVE_REGS;
776
777         startinfo.cb=sizeof(STARTUPINFO);
778         startinfo.dwFlags=STARTF_USESTDHANDLES;
779         startinfo.hStdInput=stdin_handle;
780         startinfo.hStdOutput=stdout_handle;
781         startinfo.hStdError=stderr_handle;
782         
783         shell_path = mono_string_chars (proc_start_info->filename);
784         complete_path (shell_path, &spath);
785         if (spath == NULL) {
786                 process_info->pid = -ERROR_FILE_NOT_FOUND;
787                 return FALSE;
788         }
789 #ifdef PLATFORM_WIN32
790         /* Seems like our CreateProcess does not work as the windows one.
791          * This hack is needed to deal with paths containing spaces */
792         shell_path = NULL;
793         free_shell_path = FALSE;
794         tmp = mono_string_to_utf8 (cmd);
795         newcmd = g_strdup_printf ("%s %s", spath, tmp);
796         cmd = mono_string_new_wrapper (newcmd);
797         g_free (newcmd);
798         g_free (tmp);
799 #else
800         shell_path = g_utf8_to_utf16 (spath, -1, NULL, NULL, NULL);
801 #endif
802         g_free (spath);
803
804         if (process_info->env_keys != NULL) {
805                 gint i, len; 
806                 MonoString *ms;
807                 MonoString *key, *value;
808                 gunichar2 *str, *ptr;
809                 gunichar2 *equals16;
810
811                 for (len = 0, i = 0; i < mono_array_length (process_info->env_keys); i++) {
812                         ms = mono_array_get (process_info->env_values, MonoString *, i);
813                         if (ms == NULL)
814                                 continue;
815
816                         len += mono_string_length (ms) * sizeof (gunichar2);
817                         ms = mono_array_get (process_info->env_keys, MonoString *, i);
818                         len += mono_string_length (ms) * sizeof (gunichar2);
819                         len += 2 * sizeof (gunichar2);
820                 }
821
822                 equals16 = g_utf8_to_utf16 ("=", 1, NULL, NULL, NULL);
823                 ptr = str = g_new0 (gunichar2, len + 1);
824                 for (i = 0; i < mono_array_length (process_info->env_keys); i++) {
825                         value = mono_array_get (process_info->env_values, MonoString *, i);
826                         if (value == NULL)
827                                 continue;
828
829                         key = mono_array_get (process_info->env_keys, MonoString *, i);
830                         memcpy (ptr, mono_string_chars (key), mono_string_length (key) * sizeof (gunichar2));
831                         ptr += mono_string_length (key);
832
833                         memcpy (ptr, equals16, sizeof (gunichar2));
834                         ptr++;
835
836                         memcpy (ptr, mono_string_chars (value), mono_string_length (value) * sizeof (gunichar2));
837                         ptr += mono_string_length (value);
838                         ptr++;
839                 }
840
841                 g_free (equals16);
842                 env_vars = (gchar *) str;
843         }
844         
845         /* The default dir name is "".  Turn that into NULL to mean
846          * "current directory"
847          */
848         if(mono_string_length (proc_start_info->working_directory)==0) {
849                 dir=NULL;
850         } else {
851                 dir=mono_string_chars (proc_start_info->working_directory);
852         }
853         
854         ret=CreateProcess (shell_path, mono_string_chars (cmd), NULL, NULL, TRUE, CREATE_UNICODE_ENVIRONMENT, env_vars, dir, &startinfo, &procinfo);
855
856         g_free (env_vars);
857         if (free_shell_path)
858                 g_free (shell_path);
859
860         if(ret) {
861                 process_info->process_handle=procinfo.hProcess;
862                 /*process_info->thread_handle=procinfo.hThread;*/
863                 process_info->thread_handle=NULL;
864                 if (procinfo.hThread != NULL)
865                         CloseHandle(procinfo.hThread);
866                 process_info->pid=procinfo.dwProcessId;
867                 process_info->tid=procinfo.dwThreadId;
868         } else {
869                 process_info->pid = -GetLastError ();
870         }
871         
872         return(ret);
873 }
874
875 MonoBoolean ves_icall_System_Diagnostics_Process_WaitForExit_internal (MonoObject *this, HANDLE process, gint32 ms)
876 {
877         guint32 ret;
878         
879         MONO_ARCH_SAVE_REGS;
880
881         if(ms<0) {
882                 /* Wait forever */
883                 ret=WaitForSingleObjectEx (process, INFINITE, TRUE);
884         } else {
885                 ret=WaitForSingleObjectEx (process, ms, TRUE);
886         }
887         if(ret==WAIT_OBJECT_0) {
888                 return(TRUE);
889         } else {
890                 return(FALSE);
891         }
892 }
893
894 gint64 ves_icall_System_Diagnostics_Process_ExitTime_internal (HANDLE process)
895 {
896         gboolean ret;
897         gint64 ticks;
898         FILETIME create_time, exit_time, kernel_time, user_time;
899         
900         MONO_ARCH_SAVE_REGS;
901
902         ret=GetProcessTimes (process, &create_time, &exit_time, &kernel_time,
903                              &user_time);
904         if(ret==TRUE) {
905                 ticks=((guint64)exit_time.dwHighDateTime << 32) +
906                         exit_time.dwLowDateTime;
907                 
908                 return(ticks);
909         } else {
910                 return(0);
911         }
912 }
913
914 gint64 ves_icall_System_Diagnostics_Process_StartTime_internal (HANDLE process)
915 {
916         gboolean ret;
917         gint64 ticks;
918         FILETIME create_time, exit_time, kernel_time, user_time;
919         
920         MONO_ARCH_SAVE_REGS;
921
922         ret=GetProcessTimes (process, &create_time, &exit_time, &kernel_time,
923                              &user_time);
924         if(ret==TRUE) {
925                 ticks=((guint64)create_time.dwHighDateTime << 32) +
926                         create_time.dwLowDateTime;
927                 
928                 return(ticks);
929         } else {
930                 return(0);
931         }
932 }
933
934 gint32 ves_icall_System_Diagnostics_Process_ExitCode_internal (HANDLE process)
935 {
936         guint32 code;
937         
938         MONO_ARCH_SAVE_REGS;
939
940         GetExitCodeProcess (process, &code);
941         
942 #ifdef DEBUG
943         g_message (G_GNUC_PRETTY_FUNCTION ": process exit code is %d", code);
944 #endif
945         
946         return(code);
947 }
948
949 MonoString *ves_icall_System_Diagnostics_Process_ProcessName_internal (HANDLE process)
950 {
951         MonoString *string;
952         gboolean ok;
953         HMODULE mod;
954         gunichar2 name[MAX_PATH];
955         guint32 needed;
956         guint32 len;
957         
958         MONO_ARCH_SAVE_REGS;
959
960         ok=EnumProcessModules (process, &mod, sizeof(mod), &needed);
961         if(ok==FALSE) {
962                 return(NULL);
963         }
964         
965         len=GetModuleBaseName (process, mod, name, MAX_PATH);
966         if(len==0) {
967                 return(NULL);
968         }
969         
970 #ifdef DEBUG
971         g_message (G_GNUC_PRETTY_FUNCTION ": process name is [%s]",
972                    g_utf16_to_utf8 (name, -1, NULL, NULL, NULL));
973 #endif
974         
975         string=mono_string_new_utf16 (mono_domain_get (), name, len);
976         
977         return(string);
978 }
979
980 /* Returns an array of pids */
981 MonoArray *ves_icall_System_Diagnostics_Process_GetProcesses_internal (void)
982 {
983         MonoArray *procs;
984         gboolean ret;
985         guint32 needed, count, i;
986         guint32 pids[1024];
987
988         MONO_ARCH_SAVE_REGS;
989
990         ret=EnumProcesses (pids, sizeof(pids), &needed);
991         if(ret==FALSE) {
992                 /* FIXME: throw an exception */
993                 return(NULL);
994         }
995         
996         count=needed/sizeof(guint32);
997         procs=mono_array_new (mono_domain_get (), mono_get_int32_class (),
998                               count);
999         for(i=0; i<count; i++) {
1000                 mono_array_set (procs, guint32, i, pids[i]);
1001         }
1002         
1003         return(procs);
1004 }
1005
1006 MonoBoolean ves_icall_System_Diagnostics_Process_GetWorkingSet_internal (HANDLE process, guint32 *min, guint32 *max)
1007 {
1008         gboolean ret;
1009         size_t ws_min, ws_max;
1010         
1011         MONO_ARCH_SAVE_REGS;
1012
1013         ret=GetProcessWorkingSetSize (process, &ws_min, &ws_max);
1014         *min=(guint32)ws_min;
1015         *max=(guint32)ws_max;
1016         
1017         return(ret);
1018 }
1019
1020 MonoBoolean ves_icall_System_Diagnostics_Process_SetWorkingSet_internal (HANDLE process, guint32 min, guint32 max, MonoBoolean use_min)
1021 {
1022         gboolean ret;
1023         size_t ws_min;
1024         size_t ws_max;
1025         
1026         MONO_ARCH_SAVE_REGS;
1027
1028         ret=GetProcessWorkingSetSize (process, &ws_min, &ws_max);
1029         if(ret==FALSE) {
1030                 return(FALSE);
1031         }
1032         
1033         if(use_min==TRUE) {
1034                 ws_min=min;
1035         } else {
1036                 ws_max=max;
1037         }
1038         
1039         ret=SetProcessWorkingSetSize (process, ws_min, ws_max);
1040
1041         return(ret);
1042 }
1043
1044 MonoBoolean
1045 ves_icall_System_Diagnostics_Process_Kill_internal (HANDLE process, gint32 sig)
1046 {
1047         MONO_ARCH_SAVE_REGS;
1048
1049         /* sig == 1 -> Kill, sig == 2 -> CloseMainWindow */
1050
1051         return TerminateProcess (process, -sig);
1052 }
1053