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