2004-12-29 Martin Baulig <martin@ximian.com>
[mono.git] / mono / metadata / process.c
1 /*
2  * process.c: System.Diagnostics.Process support
3  *
4  * Author:
5  *      Dick Porter (dick@ximian.com)
6  *
7  * (C) 2002 Ximian, Inc.
8  */
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 /* Only used when UseShellExecute is false */
737 static gchar *
738 quote_path (const gchar *path)
739 {
740         gchar *res = g_shell_quote (path);
741 #ifdef PLATFORM_WIN32
742         {
743         gchar *q = res;
744         while (*q) {
745                 if (*q == '\'')
746                         *q = '\"';
747                 q++;
748         }
749         }
750 #endif
751         return res;
752 }
753
754 /* Only used when UseShellExecute is false */
755 static gboolean
756 complete_path (const gunichar2 *appname, gchar **completed)
757 {
758         gchar *utf8app;
759         gchar *found;
760
761         utf8app = g_utf16_to_utf8 (appname, -1, NULL, NULL, NULL);
762         if (g_path_is_absolute (utf8app)) {
763                 *completed = quote_path (utf8app);
764                 g_free (utf8app);
765                 return TRUE;
766         }
767
768         if (g_file_test (utf8app, G_FILE_TEST_IS_EXECUTABLE) && !g_file_test (utf8app, G_FILE_TEST_IS_DIR)) {
769                 *completed = quote_path (utf8app);
770                 g_free (utf8app);
771                 return TRUE;
772         }
773         
774         found = g_find_program_in_path (utf8app);
775         if (found == NULL) {
776                 *completed = NULL;
777                 g_free (utf8app);
778                 return FALSE;
779         }
780
781         *completed = quote_path (found);
782         g_free (found);
783         g_free (utf8app);
784         return TRUE;
785 }
786
787 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)
788 {
789         gboolean ret;
790         gunichar2 *dir;
791         STARTUPINFO startinfo={0};
792         PROCESS_INFORMATION procinfo;
793         gunichar2 *shell_path = NULL;
794         gchar *env_vars = NULL;
795         gboolean free_shell_path = TRUE;
796         gchar *newcmd, *tmp;
797         
798         MONO_ARCH_SAVE_REGS;
799
800         startinfo.cb=sizeof(STARTUPINFO);
801         startinfo.dwFlags=STARTF_USESTDHANDLES;
802         startinfo.hStdInput=stdin_handle;
803         startinfo.hStdOutput=stdout_handle;
804         startinfo.hStdError=stderr_handle;
805         
806         if (process_info->use_shell) {
807                 const gchar *spath;
808                 const gchar *shell_args;
809 #ifdef PLATFORM_WIN32
810                 spath = g_getenv ("COMSPEC");
811                 shell_args = "/c %s";
812 #else
813                 spath = g_getenv ("SHELL");
814                 shell_args = "-c %s";
815 #endif
816                 if (spath != NULL) {
817                         gint dummy;
818                         gchar *quoted;
819
820                         shell_path = mono_unicode_from_external (spath, &dummy);
821                         tmp = mono_string_to_utf8 (cmd);
822                         quoted = g_shell_quote (tmp);
823 #ifdef PLATFORM_WIN32
824                         {
825                                 gchar *q = quoted;
826                                 while (*q) {
827                                         if (*q == '\'')
828                                                 *q = '\"';
829                                         q++;
830                                 }
831                         }
832 #endif
833                         newcmd = g_strdup_printf (shell_args, quoted);
834                         g_free (quoted);
835                         g_free (tmp);
836                         cmd = mono_string_new (mono_domain_get (), newcmd);
837                         g_free (newcmd);
838                 }
839         } else {
840                 gchar *spath = NULL;
841                 shell_path = mono_string_chars (appname);
842                 complete_path (shell_path, &spath);
843                 if (spath == NULL) {
844                         process_info->pid = -ERROR_FILE_NOT_FOUND;
845                         return FALSE;
846                 }
847 #ifdef PLATFORM_WIN32
848                 /* Seems like our CreateProcess does not work as the windows one.
849                  * This hack is needed to deal with paths containing spaces */
850                 shell_path = NULL;
851                 free_shell_path = FALSE;
852                 tmp = mono_string_to_utf8 (cmd);
853                 newcmd = g_strdup_printf ("%s %s", spath, tmp);
854                 cmd = mono_string_new_wrapper (newcmd);
855                 g_free (newcmd);
856                 g_free (tmp);
857 #else
858                 shell_path = g_utf8_to_utf16 (spath, -1, NULL, NULL, NULL);
859 #endif
860                 g_free (spath);
861         }
862
863         if (process_info->env_keys != NULL) {
864                 gint i, len; 
865                 MonoString *ms;
866                 MonoString *key, *value;
867                 gunichar2 *str, *ptr;
868                 gunichar2 *equals16;
869
870                 for (len = 0, i = 0; i < mono_array_length (process_info->env_keys); i++) {
871                         ms = mono_array_get (process_info->env_values, MonoString *, i);
872                         if (ms == NULL)
873                                 continue;
874
875                         len += mono_string_length (ms) * sizeof (gunichar2);
876                         ms = mono_array_get (process_info->env_keys, MonoString *, i);
877                         len += mono_string_length (ms) * sizeof (gunichar2);
878                         len += 2 * sizeof (gunichar2);
879                 }
880
881                 equals16 = g_utf8_to_utf16 ("=", 1, NULL, NULL, NULL);
882                 ptr = str = g_new0 (gunichar2, len + 1);
883                 for (i = 0; i < mono_array_length (process_info->env_keys); i++) {
884                         value = mono_array_get (process_info->env_values, MonoString *, i);
885                         if (value == NULL)
886                                 continue;
887
888                         key = mono_array_get (process_info->env_keys, MonoString *, i);
889                         memcpy (ptr, mono_string_chars (key), mono_string_length (key) * sizeof (gunichar2));
890                         ptr += mono_string_length (key);
891
892                         memcpy (ptr, equals16, sizeof (gunichar2));
893                         ptr++;
894
895                         memcpy (ptr, mono_string_chars (value), mono_string_length (value) * sizeof (gunichar2));
896                         ptr += mono_string_length (value);
897                         ptr++;
898                 }
899
900                 g_free (equals16);
901                 env_vars = (gchar *) str;
902         }
903         
904         /* The default dir name is "".  Turn that into NULL to mean
905          * "current directory"
906          */
907         if(mono_string_length (dirname)==0) {
908                 dir=NULL;
909         } else {
910                 dir=mono_string_chars (dirname);
911         }
912         
913         ret=CreateProcess (shell_path, mono_string_chars (cmd), NULL, NULL, TRUE, CREATE_UNICODE_ENVIRONMENT, env_vars, dir, &startinfo, &procinfo);
914
915         g_free (env_vars);
916         if (free_shell_path)
917                 g_free (shell_path);
918
919         if(ret) {
920                 process_info->process_handle=procinfo.hProcess;
921                 /*process_info->thread_handle=procinfo.hThread;*/
922                 process_info->thread_handle=NULL;
923                 CloseHandle(procinfo.hThread);
924                 process_info->pid=procinfo.dwProcessId;
925                 process_info->tid=procinfo.dwThreadId;
926         } else {
927                 process_info->pid = -GetLastError ();
928         }
929         
930         return(ret);
931 }
932
933 MonoBoolean ves_icall_System_Diagnostics_Process_WaitForExit_internal (MonoObject *this, HANDLE process, gint32 ms)
934 {
935         guint32 ret;
936         
937         MONO_ARCH_SAVE_REGS;
938
939         if(ms<0) {
940                 /* Wait forever */
941                 ret=WaitForSingleObjectEx (process, INFINITE, TRUE);
942         } else {
943                 ret=WaitForSingleObjectEx (process, ms, TRUE);
944         }
945         if(ret==WAIT_OBJECT_0) {
946                 return(TRUE);
947         } else {
948                 return(FALSE);
949         }
950 }
951
952 gint64 ves_icall_System_Diagnostics_Process_ExitTime_internal (HANDLE process)
953 {
954         gboolean ret;
955         gint64 ticks;
956         FILETIME create_time, exit_time, kernel_time, user_time;
957         
958         MONO_ARCH_SAVE_REGS;
959
960         ret=GetProcessTimes (process, &create_time, &exit_time, &kernel_time,
961                              &user_time);
962         if(ret==TRUE) {
963                 ticks=((guint64)exit_time.dwHighDateTime << 32) +
964                         exit_time.dwLowDateTime;
965                 
966                 return(ticks);
967         } else {
968                 return(0);
969         }
970 }
971
972 gint64 ves_icall_System_Diagnostics_Process_StartTime_internal (HANDLE process)
973 {
974         gboolean ret;
975         gint64 ticks;
976         FILETIME create_time, exit_time, kernel_time, user_time;
977         
978         MONO_ARCH_SAVE_REGS;
979
980         ret=GetProcessTimes (process, &create_time, &exit_time, &kernel_time,
981                              &user_time);
982         if(ret==TRUE) {
983                 ticks=((guint64)create_time.dwHighDateTime << 32) +
984                         create_time.dwLowDateTime;
985                 
986                 return(ticks);
987         } else {
988                 return(0);
989         }
990 }
991
992 gint32 ves_icall_System_Diagnostics_Process_ExitCode_internal (HANDLE process)
993 {
994         guint32 code;
995         
996         MONO_ARCH_SAVE_REGS;
997
998         GetExitCodeProcess (process, &code);
999         
1000 #ifdef DEBUG
1001         g_message (G_GNUC_PRETTY_FUNCTION ": process exit code is %d", code);
1002 #endif
1003         
1004         return(code);
1005 }
1006
1007 MonoString *ves_icall_System_Diagnostics_Process_ProcessName_internal (HANDLE process)
1008 {
1009         MonoString *string;
1010         gboolean ok;
1011         HMODULE mod;
1012         gunichar2 name[MAX_PATH];
1013         guint32 needed;
1014         guint32 len;
1015         
1016         MONO_ARCH_SAVE_REGS;
1017
1018         ok=EnumProcessModules (process, &mod, sizeof(mod), &needed);
1019         if(ok==FALSE) {
1020                 return(NULL);
1021         }
1022         
1023         len=GetModuleBaseName (process, mod, name, sizeof(name));
1024         if(len==0) {
1025                 return(NULL);
1026         }
1027         
1028 #ifdef DEBUG
1029         g_message (G_GNUC_PRETTY_FUNCTION ": process name is [%s]",
1030                    g_utf16_to_utf8 (name, -1, NULL, NULL, NULL));
1031 #endif
1032         
1033         string=mono_string_new_utf16 (mono_domain_get (), name, len);
1034         
1035         return(string);
1036 }
1037
1038 /* Returns an array of pids */
1039 MonoArray *ves_icall_System_Diagnostics_Process_GetProcesses_internal (void)
1040 {
1041         MonoArray *procs;
1042         gboolean ret;
1043         guint32 needed, count, i;
1044         guint32 pids[1024];
1045
1046         MONO_ARCH_SAVE_REGS;
1047
1048         ret=EnumProcesses (pids, sizeof(pids), &needed);
1049         if(ret==FALSE) {
1050                 /* FIXME: throw an exception */
1051                 return(NULL);
1052         }
1053         
1054         count=needed/sizeof(guint32);
1055         procs=mono_array_new (mono_domain_get (), mono_get_int32_class (),
1056                               count);
1057         for(i=0; i<count; i++) {
1058                 mono_array_set (procs, guint32, i, pids[i]);
1059         }
1060         
1061         return(procs);
1062 }
1063
1064 MonoBoolean ves_icall_System_Diagnostics_Process_GetWorkingSet_internal (HANDLE process, guint32 *min, guint32 *max)
1065 {
1066         gboolean ret;
1067         size_t ws_min, ws_max;
1068         
1069         MONO_ARCH_SAVE_REGS;
1070
1071         ret=GetProcessWorkingSetSize (process, &ws_min, &ws_max);
1072         *min=(guint32)ws_min;
1073         *max=(guint32)ws_max;
1074         
1075         return(ret);
1076 }
1077
1078 MonoBoolean ves_icall_System_Diagnostics_Process_SetWorkingSet_internal (HANDLE process, guint32 min, guint32 max, MonoBoolean use_min)
1079 {
1080         gboolean ret;
1081         size_t ws_min;
1082         size_t ws_max;
1083         
1084         MONO_ARCH_SAVE_REGS;
1085
1086         ret=GetProcessWorkingSetSize (process, &ws_min, &ws_max);
1087         if(ret==FALSE) {
1088                 return(FALSE);
1089         }
1090         
1091         if(use_min==TRUE) {
1092                 ws_min=min;
1093         } else {
1094                 ws_max=max;
1095         }
1096         
1097         ret=SetProcessWorkingSetSize (process, ws_min, ws_max);
1098
1099         return(ret);
1100 }
1101
1102 MonoBoolean
1103 ves_icall_System_Diagnostics_Process_Kill_internal (HANDLE process, gint32 sig)
1104 {
1105         MONO_ARCH_SAVE_REGS;
1106
1107         /* sig == 1 -> Kill, sig == 2 -> CloseMainWindow */
1108
1109         return TerminateProcess (process, -sig);
1110 }
1111