2003-11-24 Zoltan Varga <vargaz@freemail.hu>
[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/io-layer/io-layer.h>
23
24 #undef DEBUG
25
26 HANDLE ves_icall_System_Diagnostics_Process_GetProcess_internal (guint32 pid)
27 {
28         HANDLE handle;
29         
30         MONO_ARCH_SAVE_REGS;
31
32         /* GetCurrentProcess returns a pseudo-handle, so use
33          * OpenProcess instead
34          */
35         handle=OpenProcess (PROCESS_ALL_ACCESS, TRUE, pid);
36         
37         if(handle==NULL) {
38                 /* FIXME: Throw an exception */
39                 return(NULL);
40         }
41         
42         return(handle);
43 }
44
45 guint32 ves_icall_System_Diagnostics_Process_GetPid_internal (void)
46 {
47         MONO_ARCH_SAVE_REGS;
48
49         return(GetCurrentProcessId ());
50 }
51
52 void ves_icall_System_Diagnostics_Process_Process_free_internal (MonoObject *this,
53                                                                  HANDLE process)
54 {
55         MONO_ARCH_SAVE_REGS;
56
57 #ifdef THREAD_DEBUG
58         g_message (G_GNUC_PRETTY_FUNCTION ": Closing process %p, handle %p",
59                    this, process);
60 #endif
61
62         CloseHandle (process);
63 }
64
65 #define STASH_SYS_ASS(this) \
66         if(system_assembly == NULL) { \
67                 system_assembly=this->vtable->klass->image; \
68         }
69
70 static MonoImage *system_assembly=NULL;
71
72 static guint32 unicode_chars (const gunichar2 *str)
73 {
74         guint32 len=0;
75         
76         do {
77                 if(str[len]=='\0') {
78                         return(len);
79                 }
80                 len++;
81         } while(1);
82 }
83
84 static guint32 unicode_bytes (const gunichar2 *str)
85 {
86         guint32 len=0;
87         
88         do {
89                 if(str[len]=='\0') {
90                         /* Include the terminators */
91                         return((len*2)+2);
92                 }
93                 len++;
94         } while(1);
95 }
96
97 static void process_set_field_object (MonoObject *obj, const guchar *fieldname,
98                                       MonoObject *data)
99 {
100         MonoClassField *field;
101
102 #ifdef DEBUG
103         g_message (G_GNUC_PRETTY_FUNCTION ": Setting field %s to object at %p",
104                    fieldname, data);
105 #endif
106
107         field=mono_class_get_field_from_name (mono_object_class (obj),
108                                               fieldname);
109         *(MonoObject **)(((char *)obj) + field->offset)=data;
110 }
111
112 static void process_set_field_string (MonoObject *obj, const guchar *fieldname,
113                                       const gunichar2 *val, guint32 len)
114 {
115         MonoClassField *field;
116         MonoString *string;
117
118 #ifdef DEBUG
119         g_message (G_GNUC_PRETTY_FUNCTION ": Setting field %s to [%s]",
120                    fieldname, g_utf16_to_utf8 (val, len, NULL, NULL, NULL));
121 #endif
122
123         string=mono_string_new_utf16 (mono_object_domain (obj), val, len);
124         
125         field=mono_class_get_field_from_name (mono_object_class (obj),
126                                               fieldname);
127         *(MonoString **)(((char *)obj) + field->offset)=string;
128 }
129
130 static void process_set_field_string_utf8 (MonoObject *obj,
131                                            const guchar *fieldname,
132                                            const guchar *val)
133 {
134         MonoClassField *field;
135         MonoString *string;
136
137 #ifdef DEBUG
138         g_message (G_GNUC_PRETTY_FUNCTION ": Setting field %s to [%s]",
139                    fieldname, val);
140 #endif
141
142         string=mono_string_new (mono_object_domain (obj), val);
143         
144         field=mono_class_get_field_from_name (mono_object_class (obj),
145                                               fieldname);
146         *(MonoString **)(((char *)obj) + field->offset)=string;
147 }
148
149 static void process_set_field_int (MonoObject *obj, const guchar *fieldname,
150                                    guint32 val)
151 {
152         MonoClassField *field;
153
154 #ifdef DEBUG
155         g_message (G_GNUC_PRETTY_FUNCTION ": Setting field %s to %d",
156                    fieldname, val);
157 #endif
158         
159         field=mono_class_get_field_from_name (mono_object_class (obj),
160                                               fieldname);
161         *(guint32 *)(((char *)obj) + field->offset)=val;
162 }
163
164 static void process_set_field_bool (MonoObject *obj, const guchar *fieldname,
165                                     gboolean val)
166 {
167         MonoClassField *field;
168
169 #ifdef DEBUG
170         g_message (G_GNUC_PRETTY_FUNCTION ": Setting field %s to %s",
171                    fieldname, val?"TRUE":"FALSE");
172 #endif
173         
174         field=mono_class_get_field_from_name (mono_object_class (obj),
175                                               fieldname);
176         *(guint8 *)(((char *)obj) + field->offset)=val;
177 }
178
179 typedef struct {
180         guint16 data_len;
181         guint16 value_len;
182         guint16 type;
183         gunichar2 *key;
184 } version_data;
185
186 /* Returns a pointer to the value data, because theres no way to know
187  * how big that data is (value_len is set to zero for most blocks :-()
188  */
189 static gpointer process_get_versioninfo_block (gpointer data,
190                                                version_data *block)
191 {
192         block->data_len=*((guint16 *)data);
193         data = (char *)data + sizeof(guint16);
194         block->value_len=*((guint16 *)data);
195         data = (char *)data + sizeof(guint16);
196
197         /* No idea what the type is supposed to indicate */
198         block->type=*((guint16 *)data);
199         data = (char *)data + sizeof(guint16);
200         block->key=((gunichar2 *)data);
201
202         /* skip over the key (including the terminator) */
203         data=((gunichar2 *)data)+(unicode_chars (block->key)+1);
204
205         /* align on a 32-bit boundary */
206         data=(gpointer)(((unsigned)data+3) & (~3));
207         
208         return(data);
209 }
210
211 /* Returns a pointer to the byte following the Var block */
212 static gpointer process_read_var_block (MonoObject *filever, gpointer data_ptr,
213                                         guint16 data_len)
214 {
215         /* Not currently interested in the VarFileInfo block.  This
216          * might change if language support is needed for file version
217          * strings (VarFileInfo contains lists of supported
218          * languages.)
219          */
220         version_data block;
221
222         /* data_ptr is pointing at a Var block of length data_len */
223         data_ptr=process_get_versioninfo_block (data_ptr, &block);
224         data_ptr=((guchar *)data_ptr)+block.value_len;
225
226         return(data_ptr);
227 }
228
229 /* Returns a pointer to the byte following the String block, or NULL
230  * if the data read hits padding.  We can't recover from this because
231  * the data length does not include padding bytes, so it's not
232  * possible to just return the start position + length.
233  */
234 static gpointer process_read_string_block (MonoObject *filever,
235                                            gpointer data_ptr,
236                                            guint16 data_len,
237                                            gboolean store)
238 {
239         version_data block;
240         guint16 string_len=0;
241         guchar comments_key[]= {'C', '\0', 'o', '\0', 'm', '\0',
242                                 'm', '\0', 'e', '\0', 'n', '\0',
243                                 't', '\0', 's', '\0', '\0', '\0'};
244         guchar compname_key[]= {'C', '\0', 'o', '\0', 'm', '\0',
245                                 'p', '\0', 'a', '\0', 'n', '\0',
246                                 'y', '\0', 'N', '\0', 'a', '\0',
247                                 'm', '\0', 'e', '\0', '\0', '\0'};
248         guchar filedesc_key[]= {'F', '\0', 'i', '\0', 'l', '\0',
249                                 'e', '\0', 'D', '\0', 'e', '\0',
250                                 's', '\0', 'c', '\0', 'r', '\0',
251                                 'i', '\0', 'p', '\0', 't', '\0',
252                                 'i', '\0', 'o', '\0', 'n', '\0',
253                                 '\0', '\0'};
254         guchar filever_key[]= {'F', '\0', 'i', '\0', 'l', '\0',
255                                'e', '\0', 'V', '\0', 'e', '\0',
256                                'r', '\0', 's', '\0', 'i', '\0',
257                                'o', '\0', 'n', '\0', '\0', '\0'};
258         guchar internal_key[]= {'I', '\0', 'n', '\0', 't', '\0',
259                                 'e', '\0', 'r', '\0', 'n', '\0',
260                                 'a', '\0', 'l', '\0', 'N', '\0',
261                                 'a', '\0', 'm', '\0', 'e', '\0',
262                                 '\0', '\0'};
263         guchar legalcpy_key[]= {'L', '\0', 'e', '\0', 'g', '\0',
264                                 'a', '\0', 'l', '\0', 'C', '\0',
265                                 'o', '\0', 'p', '\0', 'y', '\0',
266                                 'r', '\0', 'i', '\0', 'g', '\0',
267                                 'h', '\0', 't', '\0', '\0', '\0'};
268         guchar legaltrade_key[]= {'L', '\0', 'e', '\0', 'g', '\0',
269                                   'a', '\0', 'l', '\0', 'T', '\0',
270                                   'r', '\0', 'a', '\0', 'd', '\0',
271                                   'e', '\0', 'm', '\0', 'a', '\0',
272                                   'r', '\0', 'k', '\0', 's', '\0',
273                                   '\0', '\0'};
274         guchar origfile_key[]= {'O', '\0', 'r', '\0', 'i', '\0',
275                                 'g', '\0', 'i', '\0', 'n', '\0',
276                                 'a', '\0', 'l', '\0', 'F', '\0',
277                                 'i', '\0', 'l', '\0', 'e', '\0',
278                                 'n', '\0', 'a', '\0', 'm', '\0',
279                                 'e', '\0', '\0', '\0'};
280         guchar privbuild_key[]= {'P', '\0', 'r', '\0', 'i', '\0',
281                                  'v', '\0', 'a', '\0', 't', '\0',
282                                  'e', '\0', 'B', '\0', 'u', '\0',
283                                  'i', '\0', 'l', '\0', 'd', '\0',
284                                  '\0', '\0'};
285         guchar prodname_key[]= {'P', '\0', 'r', '\0', 'o', '\0',
286                                 'd', '\0', 'u', '\0', 'c', '\0',
287                                 't', '\0', 'N', '\0', 'a', '\0',
288                                 'm', '\0', 'e', '\0', '\0', '\0'};
289         guchar prodver_key[]= {'P', '\0', 'r', '\0', 'o', '\0',
290                                'd', '\0', 'u', '\0', 'c', '\0',
291                                't', '\0', 'V', '\0', 'e', '\0',
292                                'r', '\0', 's', '\0', 'i', '\0',
293                                'o', '\0', 'n', '\0', '\0', '\0'};
294         guchar specbuild_key[]= {'S', '\0', 'p', '\0', 'e', '\0',
295                                  'c', '\0', 'i', '\0', 'a', '\0',
296                                  'l', '\0', 'B', '\0', 'u', '\0',
297                                  'i', '\0', 'l', '\0', 'd', '\0',
298                                  '\0', '\0'};
299         
300         /* data_ptr is pointing at an array of one or more String
301          * blocks with total length (not including alignment padding)
302          * of data_len.
303          */
304         while(string_len<data_len) {
305                 gunichar2 *value;
306                 
307                 /* align on a 32-bit boundary */
308                 data_ptr=(gpointer)(((unsigned)data_ptr+3) & (~3));
309
310                 data_ptr=process_get_versioninfo_block (data_ptr, &block);
311                 if(block.data_len==0) {
312                         /* We must have hit padding, so give up
313                          * processing now
314                          */
315 #ifdef DEBUG
316                         g_message (G_GNUC_PRETTY_FUNCTION
317                                    ": Hit 0-length block, giving up");
318 #endif
319                         return(NULL);
320                 }
321                 
322                 string_len=string_len+block.data_len;
323                 value=(gunichar2 *)data_ptr;
324                 /* Skip over the value */
325                 data_ptr=((gunichar2 *)data_ptr)+block.value_len;
326                 
327                 if(store==TRUE) {
328                         if(!memcmp (block.key, &comments_key,
329                                     unicode_bytes (block.key))) {
330                                 process_set_field_string (filever, "comments", value, unicode_chars (value));
331                         } else if (!memcmp (block.key, &compname_key,
332                                             unicode_bytes (block.key))) {
333                                 process_set_field_string (filever, "companyname", value, unicode_chars (value));
334                         } else if (!memcmp (block.key, &filedesc_key,
335                                             unicode_bytes (block.key))) {
336                                 process_set_field_string (filever, "filedescription", value, unicode_chars (value));
337                         } else if (!memcmp (block.key, &filever_key,
338                                             unicode_bytes (block.key))) {
339                                 process_set_field_string (filever, "fileversion", value, unicode_chars (value));
340                         } else if (!memcmp (block.key, &internal_key,
341                                             unicode_bytes (block.key))) {
342                                 process_set_field_string (filever, "internalname", value, unicode_chars (value));
343                         } else if (!memcmp (block.key, &legalcpy_key,
344                                             unicode_bytes (block.key))) {
345                                 process_set_field_string (filever, "legalcopyright", value, unicode_chars (value));
346                         } else if (!memcmp (block.key, &legaltrade_key,
347                                             unicode_bytes (block.key))) {
348                                 process_set_field_string (filever, "legaltrademarks", value, unicode_chars (value));
349                         } else if (!memcmp (block.key, &origfile_key,
350                                             unicode_bytes (block.key))) {
351                                 process_set_field_string (filever, "originalfilename", value, unicode_chars (value));
352                         } else if (!memcmp (block.key, &privbuild_key,
353                                             unicode_bytes (block.key))) {
354                                 process_set_field_string (filever, "privatebuild", value, unicode_chars (value));
355                         } else if (!memcmp (block.key, &prodname_key,
356                                             unicode_bytes (block.key))) {
357                                 process_set_field_string (filever, "productname", value, unicode_chars (value));
358                         } else if (!memcmp (block.key, &prodver_key,
359                                             unicode_bytes (block.key))) {
360                                 process_set_field_string (filever, "productversion", value, unicode_chars (value));
361                         } else if (!memcmp (block.key, &specbuild_key,
362                                             unicode_bytes (block.key))) {
363                                 process_set_field_string (filever, "specialbuild", value, unicode_chars (value));
364                         } else {
365                                 /* Not an error, just not interesting
366                                  * in this case
367                                  */
368                         }
369                 }
370         }
371         
372         return(data_ptr);
373 }
374
375 /* returns a pointer to the byte following the Stringtable block, or
376  * NULL if the data read hits padding.  We can't recover from this
377  * because the data length does not include padding bytes, so it's not
378  * possible to just return the start position + length
379  */
380 static gpointer process_read_stringtable_block (MonoObject *filever,
381                                                 gpointer data_ptr,
382                                                 guint16 data_len)
383 {
384         version_data block;
385         guint16 string_len=36;  /* length of the StringFileInfo block */
386
387         /* Specifies language-neutral unicode string block */
388         guchar uni_key[]= {'0', '\0', '0', '\0', '0', '\0', '0', '\0',
389                            '0', '\0', '4', '\0', 'b', '\0', '0', '\0',
390                            '\0', '\0'
391         };
392         guchar uni_key_uc[]= {'0', '\0', '0', '\0', '0', '\0', '0', '\0',
393                               '0', '\0', '4', '\0', 'B', '\0', '0', '\0',
394                               '\0', '\0'
395         };
396         
397         /* data_ptr is pointing at an array of StringTable blocks,
398          * with total length (not including alignment padding) of
399          * data_len.
400          */
401
402         while(string_len<data_len) {
403                 /* align on a 32-bit boundary */
404                 data_ptr=(gpointer)(((unsigned)data_ptr+3) & (~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                 if(!memcmp (block.key, &uni_key, unicode_bytes (block.key)) ||
420                    !memcmp (block.key, &uni_key_uc, unicode_bytes (block.key))) {
421                         /* Got the one we're interested in */
422                         process_set_field_string_utf8 (filever, "language",
423                                                        "Language Neutral");
424                         
425                         data_ptr=process_read_string_block (filever, data_ptr,
426                                                             block.data_len,
427                                                             TRUE);
428                 } else {
429                         /* Some other language.  We might want to do
430                          * something with this in the future.
431                          */
432                         data_ptr=process_read_string_block (filever, data_ptr,
433                                                             block.data_len,
434                                                             FALSE);
435                 }
436
437                 if(data_ptr==NULL) {
438                         /* Child block hit padding */
439 #ifdef DEBUG
440                         g_message (G_GNUC_PRETTY_FUNCTION ": Child block hit 0-length block, giving up");
441 #endif
442                         return(NULL);
443                 }
444         }
445                 
446         return(data_ptr);
447 }
448
449 static void process_read_fixedfileinfo_block (MonoObject *filever,
450                                               VS_FIXEDFILEINFO *ffi)
451 {
452 #ifdef DEBUG
453         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);
454 #endif
455                 
456         process_set_field_int (filever, "filemajorpart",
457                                HIWORD (ffi->dwFileVersionMS));
458         process_set_field_int (filever, "fileminorpart",
459                                LOWORD (ffi->dwFileVersionMS));
460         process_set_field_int (filever, "filebuildpart",
461                                HIWORD (ffi->dwFileVersionLS));
462         process_set_field_int (filever, "fileprivatepart",
463                                LOWORD (ffi->dwFileVersionLS));
464                 
465         process_set_field_int (filever, "productmajorpart",
466                                HIWORD (ffi->dwProductVersionMS));
467         process_set_field_int (filever, "productminorpart",
468                                LOWORD (ffi->dwProductVersionMS));
469         process_set_field_int (filever, "productbuildpart",
470                                HIWORD (ffi->dwProductVersionLS));
471         process_set_field_int (filever, "productprivatepart",
472                                LOWORD (ffi->dwProductVersionLS));
473         
474         process_set_field_bool (filever, "isdebug",
475                                 ffi->dwFileFlags&VS_FF_DEBUG);
476         process_set_field_bool (filever, "isprerelease",
477                                 ffi->dwFileFlags&VS_FF_PRERELEASE);
478         process_set_field_bool (filever, "ispatched",
479                                 ffi->dwFileFlags&VS_FF_PATCHED);
480         process_set_field_bool (filever, "isprivatebuild",
481                                 ffi->dwFileFlags&VS_FF_PRIVATEBUILD);
482         process_set_field_bool (filever, "isspecialbuild",
483                                 ffi->dwFileFlags&VS_FF_SPECIALBUILD);
484 }
485
486 static void process_get_fileversion (MonoObject *filever, MonoImage *image)
487 {
488         MonoPEResourceDataEntry *version_info;
489         gpointer data;
490         VS_FIXEDFILEINFO *ffi;
491         gpointer data_ptr;
492         version_data block;
493         gint32 data_len; /* signed to guard against underflow */
494         guchar vs_key[]= {'V', '\0', 'S', '\0', '_', '\0', 'V', '\0',
495                           'E', '\0', 'R', '\0', 'S', '\0', 'I', '\0',
496                           'O', '\0', 'N', '\0', '_', '\0', 'I', '\0',
497                           'N', '\0', 'F', '\0', 'O', '\0', '\0', '\0'
498         };
499         guchar var_key[]= {'V', '\0', 'a', '\0', 'r', '\0', 'F', '\0',
500                            'i', '\0', 'l', '\0', 'e', '\0', 'I', '\0',
501                            'n', '\0', 'f', '\0', 'o', '\0', '\0', '\0', 
502         };
503         guchar str_key[]= {'S', '\0', 't', '\0', 'r', '\0', 'i', '\0',
504                            'n', '\0', 'g', '\0', 'F', '\0', 'i', '\0',
505                            'l', '\0', 'e', '\0', 'I', '\0', 'n', '\0',
506                            'f', '\0', 'o', '\0', '\0', '\0', 
507         };
508         
509         version_info=mono_image_lookup_resource (image,
510                                                  MONO_PE_RESOURCE_ID_VERSION,
511                                                  0, NULL);
512 #ifdef DEBUG
513         g_message (G_GNUC_PRETTY_FUNCTION ": image_lookup returned %p",
514                    version_info);
515 #endif
516
517         if(version_info==NULL) {
518                 return;
519         }
520         
521         data=mono_cli_rva_map (image->image_info,
522                                version_info->rde_data_offset);
523         if(data==NULL) {
524                 return;
525         }
526
527         /* See io-layer/versioninfo.h for the gory details on how this
528          * data is laid out. (data should be pointing to
529          * VS_VERSIONINFO data).
530          */
531
532         data_ptr=process_get_versioninfo_block (data, &block);
533                 
534         data_len=block.data_len;
535                 
536         if(block.value_len!=sizeof(VS_FIXEDFILEINFO)) {
537 #ifdef DEBUG
538                 g_message (G_GNUC_PRETTY_FUNCTION
539                            ": FIXEDFILEINFO size mismatch");
540 #endif
541                 return;
542         }
543
544         if(memcmp (block.key, &vs_key, unicode_bytes (block.key))) {
545 #ifdef DEBUG
546                 g_message (G_GNUC_PRETTY_FUNCTION
547                            ": VS_VERSION_INFO mismatch");
548 #endif
549                 return;
550         }
551
552         ffi=((VS_FIXEDFILEINFO *)data_ptr);
553         data_ptr = (char *)data_ptr + sizeof(VS_FIXEDFILEINFO);
554         if((ffi->dwSignature!=VS_FFI_SIGNATURE) ||
555            (ffi->dwStrucVersion!=VS_FFI_STRUCVERSION)) {
556 #ifdef DEBUG
557                 g_message (G_GNUC_PRETTY_FUNCTION
558                            ": FIXEDFILEINFO bad signature");
559 #endif
560                 return;
561         }
562         process_read_fixedfileinfo_block (filever, ffi);
563         
564         /* Subtract the 92 bytes we've already seen */
565         data_len -= 92;
566         
567         /* There now follow zero or one StringFileInfo blocks and zero
568          * or one VarFileInfo blocks
569          */
570         while(data_len > 0) {
571                 /* align on a 32-bit boundary */
572                 data_ptr=(gpointer)(((unsigned)data_ptr+3) & (~3));
573
574                 data_ptr=process_get_versioninfo_block (data_ptr, &block);
575                 if(block.data_len==0) {
576                         /* We must have hit padding, so give up
577                          * processing now
578                          */
579 #ifdef DEBUG
580                         g_message (G_GNUC_PRETTY_FUNCTION
581                                    ": Hit 0-length block, giving up");
582 #endif
583                         return;
584                 }
585                 
586                 data_len=data_len-block.data_len;
587
588                 if(!memcmp (block.key, &var_key, unicode_bytes (block.key))) {
589                         data_ptr=process_read_var_block (filever, data_ptr,
590                                                          block.data_len);
591                 } else if (!memcmp (block.key, &str_key,
592                                     unicode_bytes (block.key))) {
593                         data_ptr=process_read_stringtable_block (filever, data_ptr, block.data_len);
594                 } else {
595                         /* Bogus data */
596 #ifdef DEBUG
597                         g_message (G_GNUC_PRETTY_FUNCTION
598                                    ": Not a valid VERSIONINFO child block");
599                         return;
600 #endif
601                 }
602
603                 if(data_ptr==NULL) {
604                         /* Child block hit padding */
605 #ifdef DEBUG
606                         g_message (G_GNUC_PRETTY_FUNCTION ": Child block hit 0-length block, giving up");
607 #endif
608                         return;
609                 }
610         }
611 }
612
613 static void process_add_module (GPtrArray *modules, MonoAssembly *ass)
614 {
615         MonoClass *proc_class, *filever_class;
616         MonoObject *item, *filever;
617         MonoDomain *domain=mono_domain_get ();
618         gchar *modulename;
619         
620         /* Build a System.Diagnostics.ProcessModule with the data.
621          * Leave BaseAddress and EntryPointAddress set to NULL,
622          * FileName is ass->image->name, FileVersionInfo is an object
623          * constructed from the PE image data referenced by
624          * ass->image, ModuleMemorySize set to 0, ModuleName the last
625          * component of FileName.
626          */
627         proc_class=mono_class_from_name (system_assembly, "System.Diagnostics",
628                                          "ProcessModule");
629         item=mono_object_new (domain, proc_class);
630
631         filever_class=mono_class_from_name (system_assembly,
632                                             "System.Diagnostics",
633                                             "FileVersionInfo");
634         filever=mono_object_new (domain, filever_class);
635         
636 #ifdef DEBUG
637         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);
638 #endif
639
640         process_get_fileversion (filever, ass->image);
641
642         process_set_field_string_utf8 (filever, "filename", ass->image->name);
643         process_set_field_string_utf8 (item, "filename", ass->image->name);
644         process_set_field_object (item, "version_info", filever);
645
646         modulename=g_path_get_basename (ass->image->name);
647         process_set_field_string_utf8 (item, "modulename", modulename);
648         g_free (modulename);
649
650         g_ptr_array_add (modules, item);
651 }
652
653 static void process_scan_modules (gpointer data, gpointer user_data)
654 {
655         MonoAssembly *ass=data;
656         GPtrArray *modules=user_data;
657
658         /* The main assembly is already in the list */
659         if(mono_assembly_get_main () != ass) {
660                 process_add_module (modules, ass);
661         }
662 }
663
664
665 /* Returns an array of System.Diagnostics.ProcessModule */
666 MonoArray *ves_icall_System_Diagnostics_Process_GetModules_internal (MonoObject *this)
667 {
668         /* I was going to use toolhelp for this, but then realised I
669          * was being an idiot :)
670          *
671          * (Toolhelp would give shared libraries open by the runtime,
672          * as well as open assemblies.  On windows my tests didnt find
673          * the assemblies loaded by mono either.)
674          */
675         GPtrArray *modules_list=g_ptr_array_new ();
676         MonoArray *arr;
677         guint32 i;
678         
679         MONO_ARCH_SAVE_REGS;
680
681         STASH_SYS_ASS (this);
682         
683         /* Make sure the first entry is the main module */
684         process_add_module (modules_list, mono_assembly_get_main ());
685         
686         mono_assembly_foreach (process_scan_modules, modules_list);
687
688         /* Build a MonoArray out of modules_list */
689         arr=mono_array_new (mono_domain_get (), mono_defaults.object_class,
690                             modules_list->len);
691         
692         for(i=0; i<modules_list->len; i++) {
693                 mono_array_set (arr, MonoObject *, i,
694                                 g_ptr_array_index (modules_list, i));
695         }
696         
697         g_ptr_array_free (modules_list, FALSE);
698         
699         return(arr);
700 }
701
702 void ves_icall_System_Diagnostics_FileVersionInfo_GetVersionInfo_internal (MonoObject *this, MonoString *filename)
703 {
704         MonoImage *image;
705         guchar *filename_utf8;
706         
707         MONO_ARCH_SAVE_REGS;
708
709         STASH_SYS_ASS (this);
710         
711         filename_utf8=mono_string_to_utf8 (filename);
712         image=mono_image_open (filename_utf8, NULL);
713         g_free (filename_utf8);
714         
715         if(image==NULL) {
716                 /* FIXME: an exception might be appropriate here */
717 #ifdef DEBUG
718                 g_message (G_GNUC_PRETTY_FUNCTION ": Failed to load image");
719 #endif
720
721                 return;
722         }
723         
724         process_get_fileversion (this, image);
725         process_set_field_string_utf8 (this, "filename", image->name);
726         
727         mono_image_close (image);
728 }
729
730 MonoBoolean ves_icall_System_Diagnostics_Process_Start_internal (MonoString *cmd, MonoString *dirname, HANDLE stdin_handle, HANDLE stdout_handle, HANDLE stderr_handle, MonoProcInfo *process_info)
731 {
732         gboolean ret;
733         gunichar2 *dir;
734         STARTUPINFO startinfo={0};
735         PROCESS_INFORMATION procinfo;
736         
737         MONO_ARCH_SAVE_REGS;
738
739         startinfo.cb=sizeof(STARTUPINFO);
740         startinfo.dwFlags=STARTF_USESTDHANDLES;
741         startinfo.hStdInput=stdin_handle;
742         startinfo.hStdOutput=stdout_handle;
743         startinfo.hStdError=stderr_handle;
744         
745         /* The default dir name is "".  Turn that into NULL to mean
746          * "current directory"
747          */
748         if(mono_string_length (dirname)==0) {
749                 dir=NULL;
750         } else {
751                 dir=mono_string_chars (dirname);
752         }
753         
754         ret=CreateProcess (NULL, mono_string_chars (cmd), NULL, NULL, TRUE, CREATE_UNICODE_ENVIRONMENT, NULL, dir, &startinfo, &procinfo);
755
756         if(ret) {
757                 process_info->process_handle=procinfo.hProcess;
758                 process_info->thread_handle=procinfo.hThread;
759                 process_info->pid=procinfo.dwProcessId;
760                 process_info->tid=procinfo.dwThreadId;
761         }
762         
763         return(ret);
764 }
765
766 MonoBoolean ves_icall_System_Diagnostics_Process_WaitForExit_internal (MonoObject *this, HANDLE process, gint32 ms)
767 {
768         guint32 ret;
769         
770         MONO_ARCH_SAVE_REGS;
771
772         if(ms<0) {
773                 /* Wait forever */
774                 ret=WaitForSingleObject (process, INFINITE);
775         } else {
776                 ret=WaitForSingleObject (process, ms);
777         }
778         
779         if(ret==WAIT_OBJECT_0) {
780                 return(TRUE);
781         } else {
782                 return(FALSE);
783         }
784 }
785
786 gint64 ves_icall_System_Diagnostics_Process_ExitTime_internal (HANDLE process)
787 {
788         gboolean ret;
789         gint64 ticks;
790         FILETIME create_time, exit_time, kernel_time, user_time;
791         
792         MONO_ARCH_SAVE_REGS;
793
794         ret=GetProcessTimes (process, &create_time, &exit_time, &kernel_time,
795                              &user_time);
796         if(ret==TRUE) {
797                 ticks=((guint64)exit_time.dwHighDateTime << 32) +
798                         exit_time.dwLowDateTime;
799                 
800                 return(ticks);
801         } else {
802                 return(0);
803         }
804 }
805
806 gint64 ves_icall_System_Diagnostics_Process_StartTime_internal (HANDLE process)
807 {
808         gboolean ret;
809         gint64 ticks;
810         FILETIME create_time, exit_time, kernel_time, user_time;
811         
812         MONO_ARCH_SAVE_REGS;
813
814         ret=GetProcessTimes (process, &create_time, &exit_time, &kernel_time,
815                              &user_time);
816         if(ret==TRUE) {
817                 ticks=((guint64)create_time.dwHighDateTime << 32) +
818                         create_time.dwLowDateTime;
819                 
820                 return(ticks);
821         } else {
822                 return(0);
823         }
824 }
825
826 gint32 ves_icall_System_Diagnostics_Process_ExitCode_internal (HANDLE process)
827 {
828         guint32 code;
829         
830         MONO_ARCH_SAVE_REGS;
831
832         GetExitCodeProcess (process, &code);
833         
834 #ifdef DEBUG
835         g_message (G_GNUC_PRETTY_FUNCTION ": process exit code is %d", code);
836 #endif
837         
838         return(code);
839 }
840
841 MonoString *ves_icall_System_Diagnostics_Process_ProcessName_internal (HANDLE process)
842 {
843         MonoString *string;
844         gboolean ok;
845         HMODULE mod;
846         gunichar2 name[MAX_PATH];
847         guint32 needed;
848         guint32 len;
849         
850         MONO_ARCH_SAVE_REGS;
851
852         ok=EnumProcessModules (process, &mod, sizeof(mod), &needed);
853         if(ok==FALSE) {
854                 return(NULL);
855         }
856         
857         len=GetModuleBaseName (process, mod, name, sizeof(name));
858         if(len==0) {
859                 return(NULL);
860         }
861         
862 #ifdef DEBUG
863         g_message (G_GNUC_PRETTY_FUNCTION ": process name is [%s]",
864                    g_utf16_to_utf8 (name, -1, NULL, NULL, NULL));
865 #endif
866         
867         string=mono_string_new_utf16 (mono_domain_get (), name, len);
868         
869         return(string);
870 }
871
872 /* Returns an array of pids */
873 MonoArray *ves_icall_System_Diagnostics_Process_GetProcesses_internal (void)
874 {
875         MonoArray *procs;
876         gboolean ret;
877         guint32 needed, count, i;
878         guint32 pids[1024];
879
880         MONO_ARCH_SAVE_REGS;
881
882         ret=EnumProcesses (pids, sizeof(pids), &needed);
883         if(ret==FALSE) {
884                 /* FIXME: throw an exception */
885                 return(NULL);
886         }
887         
888         count=needed/sizeof(guint32);
889         procs=mono_array_new (mono_domain_get (), mono_defaults.int_class,
890                               count);
891         for(i=0; i<count; i++) {
892                 mono_array_set (procs, guint32, i, pids[i]);
893         }
894         
895         return(procs);
896 }
897
898 MonoBoolean ves_icall_System_Diagnostics_Process_GetWorkingSet_internal (HANDLE process, guint32 *min, guint32 *max)
899 {
900         gboolean ret;
901         
902         MONO_ARCH_SAVE_REGS;
903
904         ret=GetProcessWorkingSetSize (process, min, max);
905         
906         return(ret);
907 }
908
909 MonoBoolean ves_icall_System_Diagnostics_Process_SetWorkingSet_internal (HANDLE process, guint32 min, guint32 max, MonoBoolean use_min)
910 {
911         gboolean ret;
912         guint32 ws_min;
913         guint32 ws_max;
914         
915         MONO_ARCH_SAVE_REGS;
916
917         ret=GetProcessWorkingSetSize (process, &ws_min, &ws_max);
918         if(ret==FALSE) {
919                 return(FALSE);
920         }
921         
922         if(use_min==TRUE) {
923                 max=ws_max;
924         } else {
925                 min=ws_min;
926         }
927         
928         ret=SetProcessWorkingSetSize (process, min, max);
929
930         return(ret);
931 }