2004-05-28 Gonzalo Paniagua Javier <gonzalo@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_cli_rva_map (image->image_info,
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         
615         /* Build a System.Diagnostics.ProcessModule with the data.
616          * Leave BaseAddress and EntryPointAddress set to NULL,
617          * FileName is ass->image->name, FileVersionInfo is an object
618          * constructed from the PE image data referenced by
619          * ass->image, ModuleMemorySize set to 0, ModuleName the last
620          * component of FileName.
621          */
622         proc_class=mono_class_from_name (system_assembly, "System.Diagnostics",
623                                          "ProcessModule");
624         item=mono_object_new (domain, proc_class);
625
626         filever_class=mono_class_from_name (system_assembly,
627                                             "System.Diagnostics",
628                                             "FileVersionInfo");
629         filever=mono_object_new (domain, filever_class);
630         
631 #ifdef DEBUG
632         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);
633 #endif
634
635         process_get_fileversion (filever, ass->image);
636
637         process_set_field_string_utf8 (filever, "filename", ass->image->name);
638         process_set_field_string_utf8 (item, "filename", ass->image->name);
639         process_set_field_object (item, "version_info", filever);
640
641         modulename=g_path_get_basename (ass->image->name);
642         process_set_field_string_utf8 (item, "modulename", modulename);
643         g_free (modulename);
644
645         g_ptr_array_add (modules, item);
646 }
647
648 static void process_scan_modules (gpointer data, gpointer user_data)
649 {
650         MonoAssembly *ass=data;
651         GPtrArray *modules=user_data;
652
653         /* The main assembly is already in the list */
654         if(mono_assembly_get_main () != ass) {
655                 process_add_module (modules, ass);
656         }
657 }
658
659
660 /* Returns an array of System.Diagnostics.ProcessModule */
661 MonoArray *ves_icall_System_Diagnostics_Process_GetModules_internal (MonoObject *this)
662 {
663         /* I was going to use toolhelp for this, but then realised I
664          * was being an idiot :)
665          *
666          * (Toolhelp would give shared libraries open by the runtime,
667          * as well as open assemblies.  On windows my tests didnt find
668          * the assemblies loaded by mono either.)
669          */
670         GPtrArray *modules_list=g_ptr_array_new ();
671         MonoArray *arr;
672         guint32 i;
673         
674         MONO_ARCH_SAVE_REGS;
675
676         STASH_SYS_ASS (this);
677         
678         /* Make sure the first entry is the main module */
679         process_add_module (modules_list, mono_assembly_get_main ());
680         
681         mono_assembly_foreach (process_scan_modules, modules_list);
682
683         /* Build a MonoArray out of modules_list */
684         arr=mono_array_new (mono_domain_get (), mono_defaults.object_class,
685                             modules_list->len);
686         
687         for(i=0; i<modules_list->len; i++) {
688                 mono_array_set (arr, MonoObject *, i,
689                                 g_ptr_array_index (modules_list, i));
690         }
691         
692         g_ptr_array_free (modules_list, FALSE);
693         
694         return(arr);
695 }
696
697 void ves_icall_System_Diagnostics_FileVersionInfo_GetVersionInfo_internal (MonoObject *this, MonoString *filename)
698 {
699         MonoImage *image;
700         guchar *filename_utf8;
701         
702         MONO_ARCH_SAVE_REGS;
703
704         STASH_SYS_ASS (this);
705         
706         filename_utf8=mono_string_to_utf8 (filename);
707         image=mono_image_open (filename_utf8, NULL);
708         g_free (filename_utf8);
709         
710         if(image==NULL) {
711                 /* FIXME: an exception might be appropriate here */
712 #ifdef DEBUG
713                 g_message (G_GNUC_PRETTY_FUNCTION ": Failed to load image");
714 #endif
715
716                 return;
717         }
718         
719         process_get_fileversion (this, image);
720         process_set_field_string_utf8 (this, "filename", image->name);
721         
722         mono_image_close (image);
723 }
724
725 static gboolean
726 complete_path (const gunichar2 *appname, gunichar2 **completed)
727 {
728         gchar *utf8app;
729         gchar *found;
730
731         utf8app = g_utf16_to_utf8 (appname, -1, NULL, NULL, NULL);
732         if (g_path_is_absolute (utf8app)) {
733                 g_free (utf8app);
734                 return FALSE;
735         }
736
737         if (g_file_test (utf8app, G_FILE_TEST_IS_EXECUTABLE)) {
738                 g_free (utf8app);
739                 return FALSE;
740         }
741         
742         found = g_find_program_in_path (utf8app);
743         if (found == NULL) {
744                 *completed = NULL;
745                 g_free (utf8app);
746                 return FALSE;
747         }
748
749         *completed = g_utf8_to_utf16 (found, -1, NULL, NULL, NULL);
750         g_free (found);
751         g_free (utf8app);
752         return TRUE;
753 }
754
755 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)
756 {
757         gboolean ret;
758         gunichar2 *dir;
759         STARTUPINFO startinfo={0};
760         PROCESS_INFORMATION procinfo;
761         gunichar2 *shell_path = NULL;
762         gchar *env_vars = NULL;
763         gboolean free_shell_path = TRUE;
764         
765         MONO_ARCH_SAVE_REGS;
766
767         startinfo.cb=sizeof(STARTUPINFO);
768         startinfo.dwFlags=STARTF_USESTDHANDLES;
769         startinfo.hStdInput=stdin_handle;
770         startinfo.hStdOutput=stdout_handle;
771         startinfo.hStdError=stderr_handle;
772         
773         if (process_info->use_shell) {
774                 const gchar *spath;
775                 const gchar *shell_args;
776 #ifdef PLATFORM_WIN32
777                 spath = g_getenv ("COMSPEC");
778                 shell_args = "/c %s";
779 #else
780                 spath = g_getenv ("SHELL");
781                 shell_args = "-c %s";
782 #endif
783                 if (spath != NULL) {
784                         gint dummy;
785                         gchar *newcmd, *tmp;
786                         gchar *quoted;
787
788                         shell_path = mono_unicode_from_external (spath, &dummy);
789                         tmp = mono_string_to_utf8 (cmd);
790                         quoted = g_shell_quote (tmp);
791 #ifdef PLATFORM_WIN32
792                         {
793                                 gchar *q = quoted;
794                                 while (*q) {
795                                         if (*q == '\'')
796                                                 *q = '\"';
797                                         q++;
798                                 }
799                         }
800 #endif
801                         newcmd = g_strdup_printf (shell_args, quoted);
802                         g_free (quoted);
803                         g_free (tmp);
804                         cmd = mono_string_new (mono_domain_get (), newcmd);
805                         g_free (newcmd);
806                 }
807         } else {
808                 shell_path = mono_string_chars (appname);
809                 free_shell_path = complete_path (shell_path, &shell_path);
810                 if (shell_path == NULL) {
811                         process_info->pid = -ERROR_FILE_NOT_FOUND;
812                         return FALSE;
813                 }
814         }
815
816         if (process_info->env_keys != NULL) {
817                 gint i, len; 
818                 MonoString *ms;
819                 MonoString *key, *value;
820                 gunichar2 *str, *ptr;
821                 gunichar2 *equals16;
822
823                 for (len = 0, i = 0; i < mono_array_length (process_info->env_keys); i++) {
824                         ms = mono_array_get (process_info->env_values, MonoString *, i);
825                         if (ms == NULL)
826                                 continue;
827
828                         len += mono_string_length (ms) * sizeof (gunichar2);
829                         ms = mono_array_get (process_info->env_keys, MonoString *, i);
830                         len += mono_string_length (ms) * sizeof (gunichar2);
831                         len += 2 * sizeof (gunichar2);
832                 }
833
834                 equals16 = g_utf8_to_utf16 ("=", 1, NULL, NULL, NULL);
835                 ptr = str = g_new0 (gunichar2, len + 1);
836                 for (i = 0; i < mono_array_length (process_info->env_keys); i++) {
837                         value = mono_array_get (process_info->env_values, MonoString *, i);
838                         if (value == NULL)
839                                 continue;
840
841                         key = mono_array_get (process_info->env_keys, MonoString *, i);
842                         memcpy (ptr, mono_string_chars (key), mono_string_length (key) * sizeof (gunichar2));
843                         ptr += mono_string_length (key);
844
845                         memcpy (ptr, equals16, sizeof (gunichar2));
846                         ptr++;
847
848                         memcpy (ptr, mono_string_chars (value), mono_string_length (value) * sizeof (gunichar2));
849                         ptr += mono_string_length (value);
850                         ptr++;
851                 }
852
853                 g_free (equals16);
854                 env_vars = (gchar *) str;
855         }
856         
857         /* The default dir name is "".  Turn that into NULL to mean
858          * "current directory"
859          */
860         if(mono_string_length (dirname)==0) {
861                 dir=NULL;
862         } else {
863                 dir=mono_string_chars (dirname);
864         }
865         
866         ret=CreateProcess (shell_path, mono_string_chars (cmd), NULL, NULL, TRUE, CREATE_UNICODE_ENVIRONMENT, env_vars, dir, &startinfo, &procinfo);
867
868         g_free (env_vars);
869         if (free_shell_path)
870                 g_free (shell_path);
871
872         if(ret) {
873                 process_info->process_handle=procinfo.hProcess;
874                 process_info->thread_handle=procinfo.hThread;
875                 process_info->pid=procinfo.dwProcessId;
876                 process_info->tid=procinfo.dwThreadId;
877         } else {
878                 process_info->pid = -GetLastError ();
879         }
880         
881         return(ret);
882 }
883
884 MonoBoolean ves_icall_System_Diagnostics_Process_WaitForExit_internal (MonoObject *this, HANDLE process, gint32 ms)
885 {
886         guint32 ret;
887         
888         MONO_ARCH_SAVE_REGS;
889
890         if(ms<0) {
891                 /* Wait forever */
892                 ret=WaitForSingleObjectEx (process, INFINITE, TRUE);
893         } else {
894                 ret=WaitForSingleObjectEx (process, ms, TRUE);
895         }
896         if(ret==WAIT_OBJECT_0) {
897                 return(TRUE);
898         } else {
899                 return(FALSE);
900         }
901 }
902
903 gint64 ves_icall_System_Diagnostics_Process_ExitTime_internal (HANDLE process)
904 {
905         gboolean ret;
906         gint64 ticks;
907         FILETIME create_time, exit_time, kernel_time, user_time;
908         
909         MONO_ARCH_SAVE_REGS;
910
911         ret=GetProcessTimes (process, &create_time, &exit_time, &kernel_time,
912                              &user_time);
913         if(ret==TRUE) {
914                 ticks=((guint64)exit_time.dwHighDateTime << 32) +
915                         exit_time.dwLowDateTime;
916                 
917                 return(ticks);
918         } else {
919                 return(0);
920         }
921 }
922
923 gint64 ves_icall_System_Diagnostics_Process_StartTime_internal (HANDLE process)
924 {
925         gboolean ret;
926         gint64 ticks;
927         FILETIME create_time, exit_time, kernel_time, user_time;
928         
929         MONO_ARCH_SAVE_REGS;
930
931         ret=GetProcessTimes (process, &create_time, &exit_time, &kernel_time,
932                              &user_time);
933         if(ret==TRUE) {
934                 ticks=((guint64)create_time.dwHighDateTime << 32) +
935                         create_time.dwLowDateTime;
936                 
937                 return(ticks);
938         } else {
939                 return(0);
940         }
941 }
942
943 gint32 ves_icall_System_Diagnostics_Process_ExitCode_internal (HANDLE process)
944 {
945         guint32 code;
946         
947         MONO_ARCH_SAVE_REGS;
948
949         GetExitCodeProcess (process, &code);
950         
951 #ifdef DEBUG
952         g_message (G_GNUC_PRETTY_FUNCTION ": process exit code is %d", code);
953 #endif
954         
955         return(code);
956 }
957
958 MonoString *ves_icall_System_Diagnostics_Process_ProcessName_internal (HANDLE process)
959 {
960         MonoString *string;
961         gboolean ok;
962         HMODULE mod;
963         gunichar2 name[MAX_PATH];
964         guint32 needed;
965         guint32 len;
966         
967         MONO_ARCH_SAVE_REGS;
968
969         ok=EnumProcessModules (process, &mod, sizeof(mod), &needed);
970         if(ok==FALSE) {
971                 return(NULL);
972         }
973         
974         len=GetModuleBaseName (process, mod, name, sizeof(name));
975         if(len==0) {
976                 return(NULL);
977         }
978         
979 #ifdef DEBUG
980         g_message (G_GNUC_PRETTY_FUNCTION ": process name is [%s]",
981                    g_utf16_to_utf8 (name, -1, NULL, NULL, NULL));
982 #endif
983         
984         string=mono_string_new_utf16 (mono_domain_get (), name, len);
985         
986         return(string);
987 }
988
989 /* Returns an array of pids */
990 MonoArray *ves_icall_System_Diagnostics_Process_GetProcesses_internal (void)
991 {
992         MonoArray *procs;
993         gboolean ret;
994         guint32 needed, count, i;
995         guint32 pids[1024];
996
997         MONO_ARCH_SAVE_REGS;
998
999         ret=EnumProcesses (pids, sizeof(pids), &needed);
1000         if(ret==FALSE) {
1001                 /* FIXME: throw an exception */
1002                 return(NULL);
1003         }
1004         
1005         count=needed/sizeof(guint32);
1006         procs=mono_array_new (mono_domain_get (), mono_defaults.int_class,
1007                               count);
1008         for(i=0; i<count; i++) {
1009                 mono_array_set (procs, guint32, i, pids[i]);
1010         }
1011         
1012         return(procs);
1013 }
1014
1015 MonoBoolean ves_icall_System_Diagnostics_Process_GetWorkingSet_internal (HANDLE process, guint32 *min, guint32 *max)
1016 {
1017         gboolean ret;
1018         size_t ws_min, ws_max;
1019         
1020         MONO_ARCH_SAVE_REGS;
1021
1022         ret=GetProcessWorkingSetSize (process, &ws_min, &ws_max);
1023         *min=(guint32)ws_min;
1024         *max=(guint32)ws_max;
1025         
1026         return(ret);
1027 }
1028
1029 MonoBoolean ves_icall_System_Diagnostics_Process_SetWorkingSet_internal (HANDLE process, guint32 min, guint32 max, MonoBoolean use_min)
1030 {
1031         gboolean ret;
1032         size_t ws_min;
1033         size_t ws_max;
1034         
1035         MONO_ARCH_SAVE_REGS;
1036
1037         ret=GetProcessWorkingSetSize (process, &ws_min, &ws_max);
1038         if(ret==FALSE) {
1039                 return(FALSE);
1040         }
1041         
1042         if(use_min==TRUE) {
1043                 ws_min=min;
1044         } else {
1045                 ws_max=max;
1046         }
1047         
1048         ret=SetProcessWorkingSetSize (process, ws_min, ws_max);
1049
1050         return(ret);
1051 }
1052
1053 MonoBoolean
1054 ves_icall_System_Diagnostics_Process_Kill_internal (HANDLE process, gint32 sig)
1055 {
1056         MONO_ARCH_SAVE_REGS;
1057
1058         /* sig == 1 -> Kill, sig == 2 -> CloseMainWindow */
1059
1060         return TerminateProcess (process, -sig);
1061 }
1062