Workaround from Dick to the string.Replace bug
[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
331                                 process_set_field_string (filever, "comments", value, unicode_chars (value));
332                         } else if (!memcmp (block.key, &compname_key,
333                                             unicode_bytes (block.key))) {
334                                 process_set_field_string (filever, "companyname", value, unicode_chars (value));
335                         } else if (!memcmp (block.key, &filedesc_key,
336                                             unicode_bytes (block.key))) {
337                                 process_set_field_string (filever, "filedescription", value, unicode_chars (value));
338                         } else if (!memcmp (block.key, &filever_key,
339                                             unicode_bytes (block.key))) {
340                                 process_set_field_string (filever, "fileversion", value, unicode_chars (value));
341                         } else if (!memcmp (block.key, &internal_key,
342                                             unicode_bytes (block.key))) {
343                                 process_set_field_string (filever, "internalname", value, unicode_chars (value));
344                         } else if (!memcmp (block.key, &legalcpy_key,
345                                             unicode_bytes (block.key))) {
346                                 process_set_field_string (filever, "legalcopyright", value, unicode_chars (value));
347                         } else if (!memcmp (block.key, &legaltrade_key,
348                                             unicode_bytes (block.key))) {
349                                 process_set_field_string (filever, "legaltrademarks", value, unicode_chars (value));
350                         } else if (!memcmp (block.key, &origfile_key,
351                                             unicode_bytes (block.key))) {
352                                 process_set_field_string (filever, "originalfilename", value, unicode_chars (value));
353                         } else if (!memcmp (block.key, &privbuild_key,
354                                             unicode_bytes (block.key))) {
355                                 process_set_field_string (filever, "privatebuild", value, unicode_chars (value));
356                         } else if (!memcmp (block.key, &prodname_key,
357                                             unicode_bytes (block.key))) {
358                                 process_set_field_string (filever, "productname", value, unicode_chars (value));
359                         } else if (!memcmp (block.key, &prodver_key,
360                                             unicode_bytes (block.key))) {
361                                 process_set_field_string (filever, "productversion", value, unicode_chars (value));
362                         } else if (!memcmp (block.key, &specbuild_key,
363                                             unicode_bytes (block.key))) {
364                                 process_set_field_string (filever, "specialbuild", value, unicode_chars (value));
365                         } else {
366                                 /* Not an error, just not interesting
367                                  * in this case
368                                  */
369                         }
370                 }
371         }
372         
373         return(data_ptr);
374 }
375
376 /* returns a pointer to the byte following the Stringtable block, or
377  * NULL if the data read hits padding.  We can't recover from this
378  * because the data length does not include padding bytes, so it's not
379  * possible to just return the start position + length
380  */
381 static gpointer process_read_stringtable_block (MonoObject *filever,
382                                                 gpointer data_ptr,
383                                                 guint16 data_len)
384 {
385         version_data block;
386         const char *language;
387         guint16 string_len=36;  /* length of the StringFileInfo block */
388
389         /* data_ptr is pointing at an array of StringTable blocks,
390          * with total length (not including alignment padding) of
391          * data_len.
392          */
393
394         while(string_len<data_len) {
395                 /* align on a 32-bit boundary */
396                 data_ptr=(gpointer)(((unsigned)data_ptr+3) & (~3));
397
398                 data_ptr=process_get_versioninfo_block (data_ptr, &block);
399                 if(block.data_len==0) {
400                         /* We must have hit padding, so give up
401                          * processing now
402                          */
403 #ifdef DEBUG
404                         g_message (G_GNUC_PRETTY_FUNCTION
405                                    ": Hit 0-length block, giving up");
406 #endif
407                         return(NULL);
408                 }
409                 string_len=string_len+block.data_len;
410
411                 language = g_utf16_to_utf8 (block.key, unicode_bytes (block.key), NULL, NULL, NULL);
412                 g_strdown (language);
413                 if (!strcmp (language, "007f04b0") || !strcmp (language, "000004b0")) {
414                         /* Got the one we're interested in */
415                         process_set_field_string_utf8 (filever, "language",
416                                                        "Language Neutral");
417                         
418                         data_ptr=process_read_string_block (filever, data_ptr,
419                                                             block.data_len,
420                                                             TRUE);
421                 } else {
422                         /* Some other language.  We might want to do
423                          * something with this in the future.
424                          */
425                         data_ptr=process_read_string_block (filever, data_ptr,
426                                                             block.data_len,
427                                                             FALSE);
428                 }
429                 g_free (language);
430
431                 if(data_ptr==NULL) {
432                         /* Child block hit padding */
433 #ifdef DEBUG
434                         g_message (G_GNUC_PRETTY_FUNCTION ": Child block hit 0-length block, giving up");
435 #endif
436                         return(NULL);
437                 }
438         }
439                 
440         return(data_ptr);
441 }
442
443 static void process_read_fixedfileinfo_block (MonoObject *filever,
444                                               VS_FIXEDFILEINFO *ffi)
445 {
446 #ifdef DEBUG
447         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);
448 #endif
449                 
450         process_set_field_int (filever, "filemajorpart",
451                                HIWORD (ffi->dwFileVersionMS));
452         process_set_field_int (filever, "fileminorpart",
453                                LOWORD (ffi->dwFileVersionMS));
454         process_set_field_int (filever, "filebuildpart",
455                                HIWORD (ffi->dwFileVersionLS));
456         process_set_field_int (filever, "fileprivatepart",
457                                LOWORD (ffi->dwFileVersionLS));
458                 
459         process_set_field_int (filever, "productmajorpart",
460                                HIWORD (ffi->dwProductVersionMS));
461         process_set_field_int (filever, "productminorpart",
462                                LOWORD (ffi->dwProductVersionMS));
463         process_set_field_int (filever, "productbuildpart",
464                                HIWORD (ffi->dwProductVersionLS));
465         process_set_field_int (filever, "productprivatepart",
466                                LOWORD (ffi->dwProductVersionLS));
467         
468         process_set_field_bool (filever, "isdebug",
469                                 ffi->dwFileFlags&VS_FF_DEBUG);
470         process_set_field_bool (filever, "isprerelease",
471                                 ffi->dwFileFlags&VS_FF_PRERELEASE);
472         process_set_field_bool (filever, "ispatched",
473                                 ffi->dwFileFlags&VS_FF_PATCHED);
474         process_set_field_bool (filever, "isprivatebuild",
475                                 ffi->dwFileFlags&VS_FF_PRIVATEBUILD);
476         process_set_field_bool (filever, "isspecialbuild",
477                                 ffi->dwFileFlags&VS_FF_SPECIALBUILD);
478 }
479
480 static void process_get_fileversion (MonoObject *filever, MonoImage *image)
481 {
482         MonoPEResourceDataEntry *version_info;
483         gpointer data;
484         VS_FIXEDFILEINFO *ffi;
485         gpointer data_ptr;
486         version_data block;
487         gint32 data_len; /* signed to guard against underflow */
488         guchar vs_key[]= {'V', '\0', 'S', '\0', '_', '\0', 'V', '\0',
489                           'E', '\0', 'R', '\0', 'S', '\0', 'I', '\0',
490                           'O', '\0', 'N', '\0', '_', '\0', 'I', '\0',
491                           'N', '\0', 'F', '\0', 'O', '\0', '\0', '\0'
492         };
493         guchar var_key[]= {'V', '\0', 'a', '\0', 'r', '\0', 'F', '\0',
494                            'i', '\0', 'l', '\0', 'e', '\0', 'I', '\0',
495                            'n', '\0', 'f', '\0', 'o', '\0', '\0', '\0', 
496         };
497         guchar str_key[]= {'S', '\0', 't', '\0', 'r', '\0', 'i', '\0',
498                            'n', '\0', 'g', '\0', 'F', '\0', 'i', '\0',
499                            'l', '\0', 'e', '\0', 'I', '\0', 'n', '\0',
500                            'f', '\0', 'o', '\0', '\0', '\0', 
501         };
502         
503         version_info=mono_image_lookup_resource (image,
504                                                  MONO_PE_RESOURCE_ID_VERSION,
505                                                  0, NULL);
506 #ifdef DEBUG
507         g_message (G_GNUC_PRETTY_FUNCTION ": image_lookup returned %p",
508                    version_info);
509 #endif
510
511         if(version_info==NULL) {
512                 return;
513         }
514         
515         data=mono_cli_rva_map (image->image_info,
516                                version_info->rde_data_offset);
517         if(data==NULL) {
518                 return;
519         }
520
521         /* See io-layer/versioninfo.h for the gory details on how this
522          * data is laid out. (data should be pointing to
523          * VS_VERSIONINFO data).
524          */
525
526         data_ptr=process_get_versioninfo_block (data, &block);
527                 
528         data_len=block.data_len;
529                 
530         if(block.value_len!=sizeof(VS_FIXEDFILEINFO)) {
531 #ifdef DEBUG
532                 g_message (G_GNUC_PRETTY_FUNCTION
533                            ": FIXEDFILEINFO size mismatch");
534 #endif
535                 return;
536         }
537
538         if(memcmp (block.key, &vs_key, unicode_bytes (block.key))) {
539 #ifdef DEBUG
540                 g_message (G_GNUC_PRETTY_FUNCTION
541                            ": VS_VERSION_INFO mismatch");
542 #endif
543                 return;
544         }
545
546         ffi=((VS_FIXEDFILEINFO *)data_ptr);
547         data_ptr = (char *)data_ptr + sizeof(VS_FIXEDFILEINFO);
548         if((ffi->dwSignature!=VS_FFI_SIGNATURE) ||
549            (ffi->dwStrucVersion!=VS_FFI_STRUCVERSION)) {
550 #ifdef DEBUG
551                 g_message (G_GNUC_PRETTY_FUNCTION
552                            ": FIXEDFILEINFO bad signature");
553 #endif
554                 return;
555         }
556         process_read_fixedfileinfo_block (filever, ffi);
557         
558         /* Subtract the 92 bytes we've already seen */
559         data_len -= 92;
560         
561         /* There now follow zero or one StringFileInfo blocks and zero
562          * or one VarFileInfo blocks
563          */
564         while(data_len > 0) {
565                 /* align on a 32-bit boundary */
566                 data_ptr=(gpointer)(((unsigned)data_ptr+3) & (~3));
567
568                 data_ptr=process_get_versioninfo_block (data_ptr, &block);
569                 if(block.data_len==0) {
570                         /* We must have hit padding, so give up
571                          * processing now
572                          */
573 #ifdef DEBUG
574                         g_message (G_GNUC_PRETTY_FUNCTION
575                                    ": Hit 0-length block, giving up");
576 #endif
577                         return;
578                 }
579                 
580                 data_len=data_len-block.data_len;
581
582                 if(!memcmp (block.key, &var_key, unicode_bytes (block.key))) {
583                         data_ptr=process_read_var_block (filever, data_ptr,
584                                                          block.data_len);
585                 } else if (!memcmp (block.key, &str_key,
586                                     unicode_bytes (block.key))) {
587                         data_ptr=process_read_stringtable_block (filever, data_ptr, block.data_len);
588                 } else {
589                         /* Bogus data */
590 #ifdef DEBUG
591                         g_message (G_GNUC_PRETTY_FUNCTION
592                                    ": Not a valid VERSIONINFO child block");
593                         return;
594 #endif
595                 }
596
597                 if(data_ptr==NULL) {
598                         /* Child block hit padding */
599 #ifdef DEBUG
600                         g_message (G_GNUC_PRETTY_FUNCTION ": Child block hit 0-length block, giving up");
601 #endif
602                         return;
603                 }
604         }
605 }
606
607 static void process_add_module (GPtrArray *modules, MonoAssembly *ass)
608 {
609         MonoClass *proc_class, *filever_class;
610         MonoObject *item, *filever;
611         MonoDomain *domain=mono_domain_get ();
612         gchar *modulename;
613         
614         /* Build a System.Diagnostics.ProcessModule with the data.
615          * Leave BaseAddress and EntryPointAddress set to NULL,
616          * FileName is ass->image->name, FileVersionInfo is an object
617          * constructed from the PE image data referenced by
618          * ass->image, ModuleMemorySize set to 0, ModuleName the last
619          * component of FileName.
620          */
621         proc_class=mono_class_from_name (system_assembly, "System.Diagnostics",
622                                          "ProcessModule");
623         item=mono_object_new (domain, proc_class);
624
625         filever_class=mono_class_from_name (system_assembly,
626                                             "System.Diagnostics",
627                                             "FileVersionInfo");
628         filever=mono_object_new (domain, filever_class);
629         
630 #ifdef DEBUG
631         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);
632 #endif
633
634         process_get_fileversion (filever, ass->image);
635
636         process_set_field_string_utf8 (filever, "filename", ass->image->name);
637         process_set_field_string_utf8 (item, "filename", ass->image->name);
638         process_set_field_object (item, "version_info", filever);
639
640         modulename=g_path_get_basename (ass->image->name);
641         process_set_field_string_utf8 (item, "modulename", modulename);
642         g_free (modulename);
643
644         g_ptr_array_add (modules, item);
645 }
646
647 static void process_scan_modules (gpointer data, gpointer user_data)
648 {
649         MonoAssembly *ass=data;
650         GPtrArray *modules=user_data;
651
652         /* The main assembly is already in the list */
653         if(mono_assembly_get_main () != ass) {
654                 process_add_module (modules, ass);
655         }
656 }
657
658
659 /* Returns an array of System.Diagnostics.ProcessModule */
660 MonoArray *ves_icall_System_Diagnostics_Process_GetModules_internal (MonoObject *this)
661 {
662         /* I was going to use toolhelp for this, but then realised I
663          * was being an idiot :)
664          *
665          * (Toolhelp would give shared libraries open by the runtime,
666          * as well as open assemblies.  On windows my tests didnt find
667          * the assemblies loaded by mono either.)
668          */
669         GPtrArray *modules_list=g_ptr_array_new ();
670         MonoArray *arr;
671         guint32 i;
672         
673         MONO_ARCH_SAVE_REGS;
674
675         STASH_SYS_ASS (this);
676         
677         /* Make sure the first entry is the main module */
678         process_add_module (modules_list, mono_assembly_get_main ());
679         
680         mono_assembly_foreach (process_scan_modules, modules_list);
681
682         /* Build a MonoArray out of modules_list */
683         arr=mono_array_new (mono_domain_get (), mono_defaults.object_class,
684                             modules_list->len);
685         
686         for(i=0; i<modules_list->len; i++) {
687                 mono_array_set (arr, MonoObject *, i,
688                                 g_ptr_array_index (modules_list, i));
689         }
690         
691         g_ptr_array_free (modules_list, FALSE);
692         
693         return(arr);
694 }
695
696 void ves_icall_System_Diagnostics_FileVersionInfo_GetVersionInfo_internal (MonoObject *this, MonoString *filename)
697 {
698         MonoImage *image;
699         guchar *filename_utf8;
700         
701         MONO_ARCH_SAVE_REGS;
702
703         STASH_SYS_ASS (this);
704         
705         filename_utf8=mono_string_to_utf8 (filename);
706         image=mono_image_open (filename_utf8, NULL);
707         g_free (filename_utf8);
708         
709         if(image==NULL) {
710                 /* FIXME: an exception might be appropriate here */
711 #ifdef DEBUG
712                 g_message (G_GNUC_PRETTY_FUNCTION ": Failed to load image");
713 #endif
714
715                 return;
716         }
717         
718         process_get_fileversion (this, image);
719         process_set_field_string_utf8 (this, "filename", image->name);
720         
721         mono_image_close (image);
722 }
723
724 MonoBoolean ves_icall_System_Diagnostics_Process_Start_internal (MonoString *cmd, MonoString *dirname, HANDLE stdin_handle, HANDLE stdout_handle, HANDLE stderr_handle, MonoProcInfo *process_info)
725 {
726         gboolean ret;
727         gunichar2 *dir;
728         STARTUPINFO startinfo={0};
729         PROCESS_INFORMATION procinfo;
730         
731         MONO_ARCH_SAVE_REGS;
732
733         startinfo.cb=sizeof(STARTUPINFO);
734         startinfo.dwFlags=STARTF_USESTDHANDLES;
735         startinfo.hStdInput=stdin_handle;
736         startinfo.hStdOutput=stdout_handle;
737         startinfo.hStdError=stderr_handle;
738         
739         /* The default dir name is "".  Turn that into NULL to mean
740          * "current directory"
741          */
742         if(mono_string_length (dirname)==0) {
743                 dir=NULL;
744         } else {
745                 dir=mono_string_chars (dirname);
746         }
747         
748         ret=CreateProcess (NULL, mono_string_chars (cmd), NULL, NULL, TRUE, CREATE_UNICODE_ENVIRONMENT, NULL, dir, &startinfo, &procinfo);
749
750         if(ret) {
751                 process_info->process_handle=procinfo.hProcess;
752                 process_info->thread_handle=procinfo.hThread;
753                 process_info->pid=procinfo.dwProcessId;
754                 process_info->tid=procinfo.dwThreadId;
755         } else {
756                 process_info->pid = -GetLastError ();
757         }
758         
759         return(ret);
760 }
761
762 MonoBoolean ves_icall_System_Diagnostics_Process_WaitForExit_internal (MonoObject *this, HANDLE process, gint32 ms)
763 {
764         guint32 ret;
765         
766         MONO_ARCH_SAVE_REGS;
767
768         if(ms<0) {
769                 /* Wait forever */
770                 ret=WaitForSingleObject (process, INFINITE);
771         } else {
772                 ret=WaitForSingleObject (process, ms);
773         }
774         
775         if(ret==WAIT_OBJECT_0) {
776                 return(TRUE);
777         } else {
778                 return(FALSE);
779         }
780 }
781
782 gint64 ves_icall_System_Diagnostics_Process_ExitTime_internal (HANDLE process)
783 {
784         gboolean ret;
785         gint64 ticks;
786         FILETIME create_time, exit_time, kernel_time, user_time;
787         
788         MONO_ARCH_SAVE_REGS;
789
790         ret=GetProcessTimes (process, &create_time, &exit_time, &kernel_time,
791                              &user_time);
792         if(ret==TRUE) {
793                 ticks=((guint64)exit_time.dwHighDateTime << 32) +
794                         exit_time.dwLowDateTime;
795                 
796                 return(ticks);
797         } else {
798                 return(0);
799         }
800 }
801
802 gint64 ves_icall_System_Diagnostics_Process_StartTime_internal (HANDLE process)
803 {
804         gboolean ret;
805         gint64 ticks;
806         FILETIME create_time, exit_time, kernel_time, user_time;
807         
808         MONO_ARCH_SAVE_REGS;
809
810         ret=GetProcessTimes (process, &create_time, &exit_time, &kernel_time,
811                              &user_time);
812         if(ret==TRUE) {
813                 ticks=((guint64)create_time.dwHighDateTime << 32) +
814                         create_time.dwLowDateTime;
815                 
816                 return(ticks);
817         } else {
818                 return(0);
819         }
820 }
821
822 gint32 ves_icall_System_Diagnostics_Process_ExitCode_internal (HANDLE process)
823 {
824         guint32 code;
825         
826         MONO_ARCH_SAVE_REGS;
827
828         GetExitCodeProcess (process, &code);
829         
830 #ifdef DEBUG
831         g_message (G_GNUC_PRETTY_FUNCTION ": process exit code is %d", code);
832 #endif
833         
834         return(code);
835 }
836
837 MonoString *ves_icall_System_Diagnostics_Process_ProcessName_internal (HANDLE process)
838 {
839         MonoString *string;
840         gboolean ok;
841         HMODULE mod;
842         gunichar2 name[MAX_PATH];
843         guint32 needed;
844         guint32 len;
845         
846         MONO_ARCH_SAVE_REGS;
847
848         ok=EnumProcessModules (process, &mod, sizeof(mod), &needed);
849         if(ok==FALSE) {
850                 return(NULL);
851         }
852         
853         len=GetModuleBaseName (process, mod, name, sizeof(name));
854         if(len==0) {
855                 return(NULL);
856         }
857         
858 #ifdef DEBUG
859         g_message (G_GNUC_PRETTY_FUNCTION ": process name is [%s]",
860                    g_utf16_to_utf8 (name, -1, NULL, NULL, NULL));
861 #endif
862         
863         string=mono_string_new_utf16 (mono_domain_get (), name, len);
864         
865         return(string);
866 }
867
868 /* Returns an array of pids */
869 MonoArray *ves_icall_System_Diagnostics_Process_GetProcesses_internal (void)
870 {
871         MonoArray *procs;
872         gboolean ret;
873         guint32 needed, count, i;
874         guint32 pids[1024];
875
876         MONO_ARCH_SAVE_REGS;
877
878         ret=EnumProcesses (pids, sizeof(pids), &needed);
879         if(ret==FALSE) {
880                 /* FIXME: throw an exception */
881                 return(NULL);
882         }
883         
884         count=needed/sizeof(guint32);
885         procs=mono_array_new (mono_domain_get (), mono_defaults.int_class,
886                               count);
887         for(i=0; i<count; i++) {
888                 mono_array_set (procs, guint32, i, pids[i]);
889         }
890         
891         return(procs);
892 }
893
894 MonoBoolean ves_icall_System_Diagnostics_Process_GetWorkingSet_internal (HANDLE process, guint32 *min, guint32 *max)
895 {
896         gboolean ret;
897         size_t ws_min, ws_max;
898         
899         MONO_ARCH_SAVE_REGS;
900
901         ret=GetProcessWorkingSetSize (process, &ws_min, &ws_max);
902         *min=(guint32)ws_min;
903         *max=(guint32)ws_max;
904         
905         return(ret);
906 }
907
908 MonoBoolean ves_icall_System_Diagnostics_Process_SetWorkingSet_internal (HANDLE process, guint32 min, guint32 max, MonoBoolean use_min)
909 {
910         gboolean ret;
911         size_t ws_min;
912         size_t ws_max;
913         
914         MONO_ARCH_SAVE_REGS;
915
916         ret=GetProcessWorkingSetSize (process, &ws_min, &ws_max);
917         if(ret==FALSE) {
918                 return(FALSE);
919         }
920         
921         if(use_min==TRUE) {
922                 ws_min=min;
923         } else {
924                 ws_max=max;
925         }
926         
927         ret=SetProcessWorkingSetSize (process, ws_min, ws_max);
928
929         return(ret);
930 }