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