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