2003-04-24 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/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         block->value_len=*(((guint16 *)data)++);
194
195         /* No idea what the type is supposed to indicate */
196         block->type=*(((guint16 *)data)++);
197         block->key=((gunichar2 *)data);
198
199         /* skip over the key (including the terminator) */
200         data=((gunichar2 *)data)+(unicode_chars (block->key)+1);
201
202         /* align on a 32-bit boundary */
203         data=(gpointer)(((unsigned)data+3) & (~3));
204         
205         return(data);
206 }
207
208 /* Returns a pointer to the byte following the Var block */
209 static gpointer process_read_var_block (MonoObject *filever, gpointer data_ptr,
210                                         guint16 data_len)
211 {
212         /* Not currently interested in the VarFileInfo block.  This
213          * might change if language support is needed for file version
214          * strings (VarFileInfo contains lists of supported
215          * languages.)
216          */
217         version_data block;
218
219         /* data_ptr is pointing at a Var block of length data_len */
220         data_ptr=process_get_versioninfo_block (data_ptr, &block);
221         data_ptr=((guchar *)data_ptr)+block.value_len;
222
223         return(data_ptr);
224 }
225
226 /* Returns a pointer to the byte following the String block, or NULL
227  * if the data read hits padding.  We can't recover from this because
228  * the data length does not include padding bytes, so it's not
229  * possible to just return the start position + length.
230  */
231 static gpointer process_read_string_block (MonoObject *filever,
232                                            gpointer data_ptr,
233                                            guint16 data_len,
234                                            gboolean store)
235 {
236         version_data block;
237         guint16 string_len=0;
238         guchar comments_key[]= {'C', '\0', 'o', '\0', 'm', '\0',
239                                 'm', '\0', 'e', '\0', 'n', '\0',
240                                 't', '\0', 's', '\0', '\0', '\0'};
241         guchar compname_key[]= {'C', '\0', 'o', '\0', 'm', '\0',
242                                 'p', '\0', 'a', '\0', 'n', '\0',
243                                 'y', '\0', 'N', '\0', 'a', '\0',
244                                 'm', '\0', 'e', '\0', '\0', '\0'};
245         guchar filedesc_key[]= {'F', '\0', 'i', '\0', 'l', '\0',
246                                 'e', '\0', 'D', '\0', 'e', '\0',
247                                 's', '\0', 'c', '\0', 'r', '\0',
248                                 'i', '\0', 'p', '\0', 't', '\0',
249                                 'i', '\0', 'o', '\0', 'n', '\0',
250                                 '\0', '\0'};
251         guchar filever_key[]= {'F', '\0', 'i', '\0', 'l', '\0',
252                                'e', '\0', 'V', '\0', 'e', '\0',
253                                'r', '\0', 's', '\0', 'i', '\0',
254                                'o', '\0', 'n', '\0', '\0', '\0'};
255         guchar internal_key[]= {'I', '\0', 'n', '\0', 't', '\0',
256                                 'e', '\0', 'r', '\0', 'n', '\0',
257                                 'a', '\0', 'l', '\0', 'N', '\0',
258                                 'a', '\0', 'm', '\0', 'e', '\0',
259                                 '\0', '\0'};
260         guchar legalcpy_key[]= {'L', '\0', 'e', '\0', 'g', '\0',
261                                 'a', '\0', 'l', '\0', 'C', '\0',
262                                 'o', '\0', 'p', '\0', 'y', '\0',
263                                 'r', '\0', 'i', '\0', 'g', '\0',
264                                 'h', '\0', 't', '\0', '\0', '\0'};
265         guchar legaltrade_key[]= {'L', '\0', 'e', '\0', 'g', '\0',
266                                   'a', '\0', 'l', '\0', 'T', '\0',
267                                   'r', '\0', 'a', '\0', 'd', '\0',
268                                   'e', '\0', 'm', '\0', 'a', '\0',
269                                   'r', '\0', 'k', '\0', 's', '\0',
270                                   '\0', '\0'};
271         guchar origfile_key[]= {'O', '\0', 'r', '\0', 'i', '\0',
272                                 'g', '\0', 'i', '\0', 'n', '\0',
273                                 'a', '\0', 'l', '\0', 'F', '\0',
274                                 'i', '\0', 'l', '\0', 'e', '\0',
275                                 'n', '\0', 'a', '\0', 'm', '\0',
276                                 'e', '\0', '\0', '\0'};
277         guchar privbuild_key[]= {'P', '\0', 'r', '\0', 'i', '\0',
278                                  'v', '\0', 'a', '\0', 't', '\0',
279                                  'e', '\0', 'B', '\0', 'u', '\0',
280                                  'i', '\0', 'l', '\0', 'd', '\0',
281                                  '\0', '\0'};
282         guchar prodname_key[]= {'P', '\0', 'r', '\0', 'o', '\0',
283                                 'd', '\0', 'u', '\0', 'c', '\0',
284                                 't', '\0', 'N', '\0', 'a', '\0',
285                                 'm', '\0', 'e', '\0', '\0', '\0'};
286         guchar prodver_key[]= {'P', '\0', 'r', '\0', 'o', '\0',
287                                'd', '\0', 'u', '\0', 'c', '\0',
288                                't', '\0', 'V', '\0', 'e', '\0',
289                                'r', '\0', 's', '\0', 'i', '\0',
290                                'o', '\0', 'n', '\0', '\0', '\0'};
291         guchar specbuild_key[]= {'S', '\0', 'p', '\0', 'e', '\0',
292                                  'c', '\0', 'i', '\0', 'a', '\0',
293                                  'l', '\0', 'B', '\0', 'u', '\0',
294                                  'i', '\0', 'l', '\0', 'd', '\0',
295                                  '\0', '\0'};
296         
297         /* data_ptr is pointing at an array of one or more String
298          * blocks with total length (not including alignment padding)
299          * of data_len.
300          */
301         while(string_len<data_len) {
302                 gunichar2 *value;
303                 
304                 /* align on a 32-bit boundary */
305                 data_ptr=(gpointer)(((unsigned)data_ptr+3) & (~3));
306
307                 data_ptr=process_get_versioninfo_block (data_ptr, &block);
308                 if(block.data_len==0) {
309                         /* We must have hit padding, so give up
310                          * processing now
311                          */
312 #ifdef DEBUG
313                         g_message (G_GNUC_PRETTY_FUNCTION
314                                    ": Hit 0-length block, giving up");
315 #endif
316                         return(NULL);
317                 }
318                 
319                 string_len=string_len+block.data_len;
320                 value=(gunichar2 *)data_ptr;
321                 /* Skip over the value */
322                 data_ptr=((gunichar2 *)data_ptr)+block.value_len;
323                 
324                 if(store==TRUE) {
325                         if(!memcmp (block.key, &comments_key,
326                                     unicode_bytes (block.key))) {
327                                 process_set_field_string (filever, "comments", value, unicode_chars (value));
328                         } else if (!memcmp (block.key, &compname_key,
329                                             unicode_bytes (block.key))) {
330                                 process_set_field_string (filever, "companyname", value, unicode_chars (value));
331                         } else if (!memcmp (block.key, &filedesc_key,
332                                             unicode_bytes (block.key))) {
333                                 process_set_field_string (filever, "filedescription", value, unicode_chars (value));
334                         } else if (!memcmp (block.key, &filever_key,
335                                             unicode_bytes (block.key))) {
336                                 process_set_field_string (filever, "fileversion", value, unicode_chars (value));
337                         } else if (!memcmp (block.key, &internal_key,
338                                             unicode_bytes (block.key))) {
339                                 process_set_field_string (filever, "internalname", value, unicode_chars (value));
340                         } else if (!memcmp (block.key, &legalcpy_key,
341                                             unicode_bytes (block.key))) {
342                                 process_set_field_string (filever, "legalcopyright", value, unicode_chars (value));
343                         } else if (!memcmp (block.key, &legaltrade_key,
344                                             unicode_bytes (block.key))) {
345                                 process_set_field_string (filever, "legaltrademarks", value, unicode_chars (value));
346                         } else if (!memcmp (block.key, &origfile_key,
347                                             unicode_bytes (block.key))) {
348                                 process_set_field_string (filever, "originalfilename", value, unicode_chars (value));
349                         } else if (!memcmp (block.key, &privbuild_key,
350                                             unicode_bytes (block.key))) {
351                                 process_set_field_string (filever, "privatebuild", value, unicode_chars (value));
352                         } else if (!memcmp (block.key, &prodname_key,
353                                             unicode_bytes (block.key))) {
354                                 process_set_field_string (filever, "productname", value, unicode_chars (value));
355                         } else if (!memcmp (block.key, &prodver_key,
356                                             unicode_bytes (block.key))) {
357                                 process_set_field_string (filever, "productversion", value, unicode_chars (value));
358                         } else if (!memcmp (block.key, &specbuild_key,
359                                             unicode_bytes (block.key))) {
360                                 process_set_field_string (filever, "specialbuild", value, unicode_chars (value));
361                         } else {
362                                 /* Not an error, just not interesting
363                                  * in this case
364                                  */
365                         }
366                 }
367         }
368         
369         return(data_ptr);
370 }
371
372 /* returns a pointer to the byte following the Stringtable block, or
373  * NULL if the data read hits padding.  We can't recover from this
374  * because the data length does not include padding bytes, so it's not
375  * possible to just return the start position + length
376  */
377 static gpointer process_read_stringtable_block (MonoObject *filever,
378                                                 gpointer data_ptr,
379                                                 guint16 data_len)
380 {
381         version_data block;
382         guint16 string_len=36;  /* length of the StringFileInfo block */
383
384         /* Specifies language-neutral unicode string block */
385         guchar uni_key[]= {'0', '\0', '0', '\0', '0', '\0', '0', '\0',
386                            '0', '\0', '4', '\0', 'b', '\0', '0', '\0',
387                            '\0', '\0'
388         };
389         guchar uni_key_uc[]= {'0', '\0', '0', '\0', '0', '\0', '0', '\0',
390                               '0', '\0', '4', '\0', 'B', '\0', '0', '\0',
391                               '\0', '\0'
392         };
393         
394         /* data_ptr is pointing at an array of StringTable blocks,
395          * with total length (not including alignment padding) of
396          * data_len.
397          */
398
399         while(string_len<data_len) {
400                 /* align on a 32-bit boundary */
401                 data_ptr=(gpointer)(((unsigned)data_ptr+3) & (~3));
402
403                 data_ptr=process_get_versioninfo_block (data_ptr, &block);
404                 if(block.data_len==0) {
405                         /* We must have hit padding, so give up
406                          * processing now
407                          */
408 #ifdef DEBUG
409                         g_message (G_GNUC_PRETTY_FUNCTION
410                                    ": Hit 0-length block, giving up");
411 #endif
412                         return(NULL);
413                 }
414                 string_len=string_len+block.data_len;
415         
416                 if(!memcmp (block.key, &uni_key, unicode_bytes (block.key)) ||
417                    !memcmp (block.key, &uni_key_uc, unicode_bytes (block.key))) {
418                         /* Got the one we're interested in */
419                         process_set_field_string_utf8 (filever, "language",
420                                                        "Language Neutral");
421                         
422                         data_ptr=process_read_string_block (filever, data_ptr,
423                                                             block.data_len,
424                                                             TRUE);
425                 } else {
426                         /* Some other language.  We might want to do
427                          * something with this in the future.
428                          */
429                         data_ptr=process_read_string_block (filever, data_ptr,
430                                                             block.data_len,
431                                                             FALSE);
432                 }
433
434                 if(data_ptr==NULL) {
435                         /* Child block hit padding */
436 #ifdef DEBUG
437                         g_message (G_GNUC_PRETTY_FUNCTION ": Child block hit 0-length block, giving up");
438 #endif
439                         return(NULL);
440                 }
441         }
442                 
443         return(data_ptr);
444 }
445
446 static void process_read_fixedfileinfo_block (MonoObject *filever,
447                                               VS_FIXEDFILEINFO *ffi)
448 {
449 #ifdef DEBUG
450         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);
451 #endif
452                 
453         process_set_field_int (filever, "filemajorpart",
454                                HIWORD (ffi->dwFileVersionMS));
455         process_set_field_int (filever, "fileminorpart",
456                                LOWORD (ffi->dwFileVersionMS));
457         process_set_field_int (filever, "filebuildpart",
458                                HIWORD (ffi->dwFileVersionLS));
459         process_set_field_int (filever, "fileprivatepart",
460                                LOWORD (ffi->dwFileVersionLS));
461                 
462         process_set_field_int (filever, "productmajorpart",
463                                HIWORD (ffi->dwProductVersionMS));
464         process_set_field_int (filever, "productminorpart",
465                                LOWORD (ffi->dwProductVersionMS));
466         process_set_field_int (filever, "productbuildpart",
467                                HIWORD (ffi->dwProductVersionLS));
468         process_set_field_int (filever, "productprivatepart",
469                                LOWORD (ffi->dwProductVersionLS));
470         
471         process_set_field_bool (filever, "isdebug",
472                                 ffi->dwFileFlags&VS_FF_DEBUG);
473         process_set_field_bool (filever, "isprerelease",
474                                 ffi->dwFileFlags&VS_FF_PRERELEASE);
475         process_set_field_bool (filever, "ispatched",
476                                 ffi->dwFileFlags&VS_FF_PATCHED);
477         process_set_field_bool (filever, "isprivatebuild",
478                                 ffi->dwFileFlags&VS_FF_PRIVATEBUILD);
479         process_set_field_bool (filever, "isspecialbuild",
480                                 ffi->dwFileFlags&VS_FF_SPECIALBUILD);
481 }
482
483 static void process_get_fileversion (MonoObject *filever, MonoImage *image)
484 {
485         MonoPEResourceDataEntry *version_info;
486         gpointer data;
487         VS_FIXEDFILEINFO *ffi;
488         gpointer data_ptr;
489         version_data block;
490         gint32 data_len; /* signed to guard against underflow */
491         guchar vs_key[]= {'V', '\0', 'S', '\0', '_', '\0', 'V', '\0',
492                           'E', '\0', 'R', '\0', 'S', '\0', 'I', '\0',
493                           'O', '\0', 'N', '\0', '_', '\0', 'I', '\0',
494                           'N', '\0', 'F', '\0', 'O', '\0', '\0', '\0'
495         };
496         guchar var_key[]= {'V', '\0', 'a', '\0', 'r', '\0', 'F', '\0',
497                            'i', '\0', 'l', '\0', 'e', '\0', 'I', '\0',
498                            'n', '\0', 'f', '\0', 'o', '\0', '\0', '\0', 
499         };
500         guchar str_key[]= {'S', '\0', 't', '\0', 'r', '\0', 'i', '\0',
501                            'n', '\0', 'g', '\0', 'F', '\0', 'i', '\0',
502                            'l', '\0', 'e', '\0', 'I', '\0', 'n', '\0',
503                            'f', '\0', 'o', '\0', '\0', '\0', 
504         };
505         
506         version_info=mono_image_lookup_resource (image,
507                                                  MONO_PE_RESOURCE_ID_VERSION,
508                                                  0, NULL);
509 #ifdef DEBUG
510         g_message (G_GNUC_PRETTY_FUNCTION ": image_lookup returned %p",
511                    version_info);
512 #endif
513
514         if(version_info==NULL) {
515                 return;
516         }
517         
518         data=mono_cli_rva_map (image->image_info,
519                                version_info->rde_data_offset);
520         if(data==NULL) {
521                 return;
522         }
523
524         /* See io-layer/versioninfo.h for the gory details on how this
525          * data is laid out. (data should be pointing to
526          * VS_VERSIONINFO data).
527          */
528
529         data_ptr=process_get_versioninfo_block (data, &block);
530                 
531         data_len=block.data_len;
532                 
533         if(block.value_len!=sizeof(VS_FIXEDFILEINFO)) {
534 #ifdef DEBUG
535                 g_message (G_GNUC_PRETTY_FUNCTION
536                            ": FIXEDFILEINFO size mismatch");
537 #endif
538                 return;
539         }
540
541         if(memcmp (block.key, &vs_key, unicode_bytes (block.key))) {
542 #ifdef DEBUG
543                 g_message (G_GNUC_PRETTY_FUNCTION
544                            ": VS_VERSION_INFO mismatch");
545 #endif
546                 return;
547         }
548
549         ffi=(((VS_FIXEDFILEINFO *)data_ptr)++);
550         if((ffi->dwSignature!=VS_FFI_SIGNATURE) ||
551            (ffi->dwStrucVersion!=VS_FFI_STRUCVERSION)) {
552 #ifdef DEBUG
553                 g_message (G_GNUC_PRETTY_FUNCTION
554                            ": FIXEDFILEINFO bad signature");
555 #endif
556                 return;
557         }
558         process_read_fixedfileinfo_block (filever, ffi);
559         
560         /* Subtract the 92 bytes we've already seen */
561         data_len -= 92;
562         
563         /* There now follow zero or one StringFileInfo blocks and zero
564          * or one VarFileInfo blocks
565          */
566         while(data_len > 0) {
567                 /* align on a 32-bit boundary */
568                 data_ptr=(gpointer)(((unsigned)data_ptr+3) & (~3));
569
570                 data_ptr=process_get_versioninfo_block (data_ptr, &block);
571                 if(block.data_len==0) {
572                         /* We must have hit padding, so give up
573                          * processing now
574                          */
575 #ifdef DEBUG
576                         g_message (G_GNUC_PRETTY_FUNCTION
577                                    ": Hit 0-length block, giving up");
578 #endif
579                         return;
580                 }
581                 
582                 data_len=data_len-block.data_len;
583
584                 if(!memcmp (block.key, &var_key, unicode_bytes (block.key))) {
585                         data_ptr=process_read_var_block (filever, data_ptr,
586                                                          block.data_len);
587                 } else if (!memcmp (block.key, &str_key,
588                                     unicode_bytes (block.key))) {
589                         data_ptr=process_read_stringtable_block (filever, data_ptr, block.data_len);
590                 } else {
591                         /* Bogus data */
592 #ifdef DEBUG
593                         g_message (G_GNUC_PRETTY_FUNCTION
594                                    ": Not a valid VERSIONINFO child block");
595                         return;
596 #endif
597                 }
598
599                 if(data_ptr==NULL) {
600                         /* Child block hit padding */
601 #ifdef DEBUG
602                         g_message (G_GNUC_PRETTY_FUNCTION ": Child block hit 0-length block, giving up");
603 #endif
604                         return;
605                 }
606         }
607 }
608
609 static void process_add_module (GPtrArray *modules, MonoAssembly *ass)
610 {
611         MonoClass *proc_class, *filever_class;
612         MonoObject *item, *filever;
613         MonoDomain *domain=mono_domain_get ();
614         gchar *modulename;
615         
616         /* Build a System.Diagnostics.ProcessModule with the data.
617          * Leave BaseAddress and EntryPointAddress set to NULL,
618          * FileName is ass->image->name, FileVersionInfo is an object
619          * constructed from the PE image data referenced by
620          * ass->image, ModuleMemorySize set to 0, ModuleName the last
621          * component of FileName.
622          */
623         proc_class=mono_class_from_name (system_assembly, "System.Diagnostics",
624                                          "ProcessModule");
625         item=mono_object_new (domain, proc_class);
626
627         filever_class=mono_class_from_name (system_assembly,
628                                             "System.Diagnostics",
629                                             "FileVersionInfo");
630         filever=mono_object_new (domain, filever_class);
631         
632 #ifdef DEBUG
633         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);
634 #endif
635
636         process_get_fileversion (filever, ass->image);
637
638         process_set_field_string_utf8 (filever, "filename", ass->image->name);
639         process_set_field_string_utf8 (item, "filename", ass->image->name);
640         process_set_field_object (item, "version_info", filever);
641
642         modulename=g_path_get_basename (ass->image->name);
643         process_set_field_string_utf8 (item, "modulename", modulename);
644         g_free (modulename);
645
646         g_ptr_array_add (modules, item);
647 }
648
649 static void process_scan_modules (gpointer data, gpointer user_data)
650 {
651         MonoAssembly *ass=data;
652         GPtrArray *modules=user_data;
653
654         /* The main assembly is already in the list */
655         if(mono_assembly_get_main () != ass) {
656                 process_add_module (modules, ass);
657         }
658 }
659
660
661 /* Returns an array of System.Diagnostics.ProcessModule */
662 MonoArray *ves_icall_System_Diagnostics_Process_GetModules_internal (MonoObject *this)
663 {
664         /* I was going to use toolhelp for this, but then realised I
665          * was being an idiot :)
666          *
667          * (Toolhelp would give shared libraries open by the runtime,
668          * as well as open assemblies.  On windows my tests didnt find
669          * the assemblies loaded by mono either.)
670          */
671         GPtrArray *modules_list=g_ptr_array_new ();
672         MonoArray *arr;
673         guint32 i;
674         
675         MONO_ARCH_SAVE_REGS;
676
677         STASH_SYS_ASS (this);
678         
679         /* Make sure the first entry is the main module */
680         process_add_module (modules_list, mono_assembly_get_main ());
681         
682         mono_assembly_foreach (process_scan_modules, modules_list);
683
684         /* Build a MonoArray out of modules_list */
685         arr=mono_array_new (mono_domain_get (), mono_defaults.object_class,
686                             modules_list->len);
687         
688         for(i=0; i<modules_list->len; i++) {
689                 mono_array_set (arr, MonoObject *, i,
690                                 g_ptr_array_index (modules_list, i));
691         }
692         
693         g_ptr_array_free (modules_list, FALSE);
694         
695         return(arr);
696 }
697
698 void ves_icall_System_Diagnostics_FileVersionInfo_GetVersionInfo_internal (MonoObject *this, MonoString *filename)
699 {
700         MonoImage *image;
701         guchar *filename_utf8;
702         
703         MONO_ARCH_SAVE_REGS;
704
705         STASH_SYS_ASS (this);
706         
707         filename_utf8=mono_string_to_utf8 (filename);
708         image=mono_image_open (filename_utf8, NULL);
709         g_free (filename_utf8);
710         
711         if(image==NULL) {
712                 /* FIXME: an exception might be appropriate here */
713 #ifdef DEBUG
714                 g_message (G_GNUC_PRETTY_FUNCTION ": Failed to load image");
715 #endif
716
717                 return;
718         }
719         
720         process_get_fileversion (this, image);
721         process_set_field_string_utf8 (this, "filename", image->name);
722         
723         mono_image_close (image);
724 }
725
726 MonoBoolean ves_icall_System_Diagnostics_Process_Start_internal (MonoString *cmd, MonoString *dirname, HANDLE stdin_handle, HANDLE stdout_handle, HANDLE stderr_handle, MonoProcInfo *process_info)
727 {
728         gboolean ret;
729         gunichar2 *dir;
730         STARTUPINFO startinfo={0};
731         PROCESS_INFORMATION procinfo;
732         
733         MONO_ARCH_SAVE_REGS;
734
735         startinfo.cb=sizeof(STARTUPINFO);
736         startinfo.dwFlags=STARTF_USESTDHANDLES;
737         startinfo.hStdInput=stdin_handle;
738         startinfo.hStdOutput=stdout_handle;
739         startinfo.hStdError=stderr_handle;
740         
741         /* The default dir name is "".  Turn that into NULL to mean
742          * "current directory"
743          */
744         if(mono_string_length (dirname)==0) {
745                 dir=NULL;
746         } else {
747                 dir=mono_string_chars (dirname);
748         }
749         
750         ret=CreateProcess (NULL, mono_string_chars (cmd), NULL, NULL, TRUE, CREATE_UNICODE_ENVIRONMENT, NULL, dir, &startinfo, &procinfo);
751
752         if(ret) {
753                 process_info->process_handle=procinfo.hProcess;
754                 process_info->thread_handle=procinfo.hThread;
755                 process_info->pid=procinfo.dwProcessId;
756                 process_info->tid=procinfo.dwThreadId;
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         
898         MONO_ARCH_SAVE_REGS;
899
900         ret=GetProcessWorkingSetSize (process, min, max);
901         
902         return(ret);
903 }
904
905 MonoBoolean ves_icall_System_Diagnostics_Process_SetWorkingSet_internal (HANDLE process, guint32 min, guint32 max, MonoBoolean use_min)
906 {
907         gboolean ret;
908         guint32 ws_min;
909         guint32 ws_max;
910         
911         MONO_ARCH_SAVE_REGS;
912
913         ret=GetProcessWorkingSetSize (process, &ws_min, &ws_max);
914         if(ret==FALSE) {
915                 return(FALSE);
916         }
917         
918         if(use_min==TRUE) {
919                 max=ws_max;
920         } else {
921                 min=ws_min;
922         }
923         
924         ret=SetProcessWorkingSetSize (process, min, max);
925
926         return(ret);
927 }