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