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