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