2002-09-27 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_GetProcess_internal (guint32 pid)
25 {
26         HANDLE handle;
27         
28         /* GetCurrentProcess returns a pseudo-handle, so use
29          * OpenProcess instead
30          */
31         handle=OpenProcess (PROCESS_ALL_ACCESS, TRUE, pid);
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 *cmd, MonoString *dirname, HANDLE stdin_handle, HANDLE stdout_handle, HANDLE stderr_handle, MonoProcInfo *process_info)
659 {
660         gboolean ret;
661         gunichar2 *dir;
662         STARTUPINFO startinfo={0};
663         PROCESS_INFORMATION procinfo;
664         
665         startinfo.cb=sizeof(STARTUPINFO);
666         startinfo.dwFlags=STARTF_USESTDHANDLES;
667         startinfo.hStdInput=stdin_handle;
668         startinfo.hStdOutput=stdout_handle;
669         startinfo.hStdError=stderr_handle;
670         
671         /* The default dir name is "".  Turn that into NULL to mean
672          * "current directory"
673          */
674         if(mono_string_length (dirname)==0) {
675                 dir=NULL;
676         } else {
677                 dir=mono_string_chars (dirname);
678         }
679         
680         ret=CreateProcess (NULL, mono_string_chars (cmd), NULL, NULL, TRUE, CREATE_UNICODE_ENVIRONMENT, NULL, dir, &startinfo, &procinfo);
681
682         if(ret==TRUE) {
683                 process_info->process_handle=procinfo.hProcess;
684                 process_info->thread_handle=procinfo.hThread;
685                 process_info->pid=procinfo.dwProcessId;
686                 process_info->tid=procinfo.dwThreadId;
687         }
688         
689         return(ret);
690 }
691
692 MonoBoolean ves_icall_System_Diagnostics_Process_WaitForExit_internal (MonoObject *this, HANDLE process, gint32 ms)
693 {
694         guint32 ret;
695         
696         if(ms<0) {
697                 /* Wait forever */
698                 ret=WaitForSingleObject (process, INFINITE);
699         } else {
700                 ret=WaitForSingleObject (process, ms);
701         }
702         
703         if(ret==WAIT_OBJECT_0) {
704                 return(TRUE);
705         } else {
706                 return(FALSE);
707         }
708 }
709
710 gint64 ves_icall_System_Diagnostics_Process_ExitTime_internal (HANDLE process)
711 {
712         gboolean ret;
713         gint64 ticks;
714         FILETIME create_time, exit_time, kernel_time, user_time;
715         
716         ret=GetProcessTimes (process, &create_time, &exit_time, &kernel_time,
717                              &user_time);
718         if(ret==TRUE) {
719                 ticks=((guint64)exit_time.dwHighDateTime << 32) +
720                         exit_time.dwLowDateTime;
721                 
722                 return(ticks);
723         } else {
724                 return(0);
725         }
726 }
727
728 gint64 ves_icall_System_Diagnostics_Process_StartTime_internal (HANDLE process)
729 {
730         gboolean ret;
731         gint64 ticks;
732         FILETIME create_time, exit_time, kernel_time, user_time;
733         
734         ret=GetProcessTimes (process, &create_time, &exit_time, &kernel_time,
735                              &user_time);
736         if(ret==TRUE) {
737                 ticks=((guint64)create_time.dwHighDateTime << 32) +
738                         create_time.dwLowDateTime;
739                 
740                 return(ticks);
741         } else {
742                 return(0);
743         }
744 }
745
746 gint32 ves_icall_System_Diagnostics_Process_ExitCode_internal (HANDLE process)
747 {
748         guint32 code;
749         
750         GetExitCodeProcess (process, &code);
751         
752 #ifdef DEBUG
753         g_message (G_GNUC_PRETTY_FUNCTION ": process exit code is %d", code);
754 #endif
755         
756         return(code);
757 }
758
759 MonoString *ves_icall_System_Diagnostics_Process_ProcessName_internal (HANDLE process)
760 {
761         MonoString *string;
762         gboolean ok;
763         HMODULE mod;
764         gunichar2 name[MAX_PATH];
765         guint32 needed;
766         guint32 len;
767         
768         ok=EnumProcessModules (process, &mod, sizeof(mod), &needed);
769         if(ok==FALSE) {
770                 return(NULL);
771         }
772         
773         len=GetModuleBaseName (process, mod, name, sizeof(name));
774         if(len==0) {
775                 return(NULL);
776         }
777         
778 #ifdef DEBUG
779         g_message (G_GNUC_PRETTY_FUNCTION ": process name is [%s]",
780                    g_utf16_to_utf8 (name, -1, NULL, NULL, NULL));
781 #endif
782         
783         string=mono_string_new_utf16 (mono_domain_get (), name, len);
784         
785         return(string);
786 }
787
788 /* Returns an array of pids */
789 MonoArray *ves_icall_System_Diagnostics_Process_GetProcesses_internal (void)
790 {
791         MonoArray *procs;
792         gboolean ret;
793         guint32 needed, count, i;
794         guint32 pids[1024];
795
796         ret=EnumProcesses (pids, sizeof(pids), &needed);
797         if(ret==FALSE) {
798                 /* FIXME: throw an exception */
799                 return(NULL);
800         }
801         
802         count=needed/sizeof(guint32);
803         procs=mono_array_new (mono_domain_get (), mono_defaults.int_class,
804                               count);
805         for(i=0; i<count; i++) {
806                 mono_array_set (procs, guint32, i, pids[i]);
807         }
808         
809         return(procs);
810 }
811
812 MonoBoolean ves_icall_System_Diagnostics_Process_GetWorkingSet_internal (HANDLE process, guint32 *min, guint32 *max)
813 {
814         gboolean ret;
815         
816         ret=GetProcessWorkingSetSize (process, min, max);
817         
818         return(ret);
819 }
820
821 MonoBoolean ves_icall_System_Diagnostics_Process_SetWorkingSet_internal (HANDLE process, guint32 min, guint32 max, MonoBoolean use_min)
822 {
823         gboolean ret;
824         guint32 ws_min;
825         guint32 ws_max;
826         
827         ret=GetProcessWorkingSetSize (process, &ws_min, &ws_max);
828         if(ret==FALSE) {
829                 return(FALSE);
830         }
831         
832         if(use_min==TRUE) {
833                 max=ws_max;
834         } else {
835                 min=ws_min;
836         }
837         
838         ret=SetProcessWorkingSetSize (process, min, max);
839
840         return(ret);
841 }