2004-05-03 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 MonoBoolean ves_icall_System_Diagnostics_Process_Start_internal (MonoString *cmd, MonoString *dirname, HANDLE stdin_handle, HANDLE stdout_handle, HANDLE stderr_handle, MonoProcInfo *process_info)
726 {
727         gboolean ret;
728         gunichar2 *dir;
729         STARTUPINFO startinfo={0};
730         PROCESS_INFORMATION procinfo;
731         gunichar2 *shell_path = NULL;
732         gchar *env_vars = NULL;
733         
734         MONO_ARCH_SAVE_REGS;
735
736         startinfo.cb=sizeof(STARTUPINFO);
737         startinfo.dwFlags=STARTF_USESTDHANDLES;
738         startinfo.hStdInput=stdin_handle;
739         startinfo.hStdOutput=stdout_handle;
740         startinfo.hStdError=stderr_handle;
741         
742         if (process_info->use_shell) {
743                 const gchar *spath;
744                 gchar *shell_args;
745 #ifdef PLATFORM_WIN32
746                 spath = g_getenv ("COMSPEC");
747                 shell_args = "/c %s";
748 #else
749                 spath = g_getenv ("SHELL");
750                 shell_args = "-c %s";
751 #endif
752                 if (spath != NULL) {
753                         gint dummy;
754                         gchar *newcmd, *tmp;
755                         gchar *quoted;
756
757                         shell_path = mono_unicode_from_external (spath, &dummy);
758                         tmp = mono_string_to_utf8 (cmd);
759                         quoted = g_shell_quote (tmp);
760                         newcmd = g_strdup_printf (shell_args, quoted);
761                         g_free (quoted);
762                         g_free (tmp);
763                         cmd = mono_string_new (mono_domain_get (), newcmd);
764                         g_free (newcmd);
765                 }
766         }
767
768         if (process_info->env_keys != NULL) {
769                 gint i, len; 
770                 MonoString *ms;
771                 MonoString *key, *value;
772                 gunichar2 *str, *ptr;
773                 gunichar2 *equals16;
774
775                 for (len = 0, i = 0; i < mono_array_length (process_info->env_keys); i++) {
776                         ms = mono_array_get (process_info->env_values, MonoString *, i);
777                         if (ms == NULL)
778                                 continue;
779
780                         len += mono_string_length (ms) * sizeof (gunichar2);
781                         ms = mono_array_get (process_info->env_keys, MonoString *, i);
782                         len += mono_string_length (ms) * sizeof (gunichar2);
783                         len += 2 * sizeof (gunichar2);
784                 }
785
786                 equals16 = g_utf8_to_utf16 ("=", 1, NULL, NULL, NULL);
787                 ptr = str = g_new0 (gunichar2, len + 1);
788                 for (i = 0; i < mono_array_length (process_info->env_keys); i++) {
789                         value = mono_array_get (process_info->env_values, MonoString *, i);
790                         if (value == NULL)
791                                 continue;
792
793                         key = mono_array_get (process_info->env_keys, MonoString *, i);
794                         memcpy (ptr, mono_string_chars (key), mono_string_length (key) * sizeof (gunichar2));
795                         ptr += mono_string_length (key);
796
797                         memcpy (ptr, equals16, sizeof (gunichar2));
798                         ptr++;
799
800                         memcpy (ptr, mono_string_chars (value), mono_string_length (value) * sizeof (gunichar2));
801                         ptr += mono_string_length (value);
802                         ptr++;
803                 }
804
805                 g_free (equals16);
806                 env_vars = (gchar *) str;
807         }
808         
809         /* The default dir name is "".  Turn that into NULL to mean
810          * "current directory"
811          */
812         if(mono_string_length (dirname)==0) {
813                 dir=NULL;
814         } else {
815                 dir=mono_string_chars (dirname);
816         }
817         
818         ret=CreateProcess (shell_path, mono_string_chars (cmd), NULL, NULL, TRUE, CREATE_UNICODE_ENVIRONMENT, env_vars, dir, &startinfo, &procinfo);
819
820         g_free (env_vars);
821
822         if(ret) {
823                 process_info->process_handle=procinfo.hProcess;
824                 process_info->thread_handle=procinfo.hThread;
825                 process_info->pid=procinfo.dwProcessId;
826                 process_info->tid=procinfo.dwThreadId;
827         } else {
828                 process_info->pid = -GetLastError ();
829         }
830         
831         return(ret);
832 }
833
834 MonoBoolean ves_icall_System_Diagnostics_Process_WaitForExit_internal (MonoObject *this, HANDLE process, gint32 ms)
835 {
836         guint32 ret;
837         
838         MONO_ARCH_SAVE_REGS;
839
840         if(ms<0) {
841                 /* Wait forever */
842                 ret=WaitForSingleObject (process, INFINITE);
843         } else {
844                 ret=WaitForSingleObject (process, ms);
845         }
846         
847         if(ret==WAIT_OBJECT_0) {
848                 return(TRUE);
849         } else {
850                 return(FALSE);
851         }
852 }
853
854 gint64 ves_icall_System_Diagnostics_Process_ExitTime_internal (HANDLE process)
855 {
856         gboolean ret;
857         gint64 ticks;
858         FILETIME create_time, exit_time, kernel_time, user_time;
859         
860         MONO_ARCH_SAVE_REGS;
861
862         ret=GetProcessTimes (process, &create_time, &exit_time, &kernel_time,
863                              &user_time);
864         if(ret==TRUE) {
865                 ticks=((guint64)exit_time.dwHighDateTime << 32) +
866                         exit_time.dwLowDateTime;
867                 
868                 return(ticks);
869         } else {
870                 return(0);
871         }
872 }
873
874 gint64 ves_icall_System_Diagnostics_Process_StartTime_internal (HANDLE process)
875 {
876         gboolean ret;
877         gint64 ticks;
878         FILETIME create_time, exit_time, kernel_time, user_time;
879         
880         MONO_ARCH_SAVE_REGS;
881
882         ret=GetProcessTimes (process, &create_time, &exit_time, &kernel_time,
883                              &user_time);
884         if(ret==TRUE) {
885                 ticks=((guint64)create_time.dwHighDateTime << 32) +
886                         create_time.dwLowDateTime;
887                 
888                 return(ticks);
889         } else {
890                 return(0);
891         }
892 }
893
894 gint32 ves_icall_System_Diagnostics_Process_ExitCode_internal (HANDLE process)
895 {
896         guint32 code;
897         
898         MONO_ARCH_SAVE_REGS;
899
900         GetExitCodeProcess (process, &code);
901         
902 #ifdef DEBUG
903         g_message (G_GNUC_PRETTY_FUNCTION ": process exit code is %d", code);
904 #endif
905         
906         return(code);
907 }
908
909 MonoString *ves_icall_System_Diagnostics_Process_ProcessName_internal (HANDLE process)
910 {
911         MonoString *string;
912         gboolean ok;
913         HMODULE mod;
914         gunichar2 name[MAX_PATH];
915         guint32 needed;
916         guint32 len;
917         
918         MONO_ARCH_SAVE_REGS;
919
920         ok=EnumProcessModules (process, &mod, sizeof(mod), &needed);
921         if(ok==FALSE) {
922                 return(NULL);
923         }
924         
925         len=GetModuleBaseName (process, mod, name, sizeof(name));
926         if(len==0) {
927                 return(NULL);
928         }
929         
930 #ifdef DEBUG
931         g_message (G_GNUC_PRETTY_FUNCTION ": process name is [%s]",
932                    g_utf16_to_utf8 (name, -1, NULL, NULL, NULL));
933 #endif
934         
935         string=mono_string_new_utf16 (mono_domain_get (), name, len);
936         
937         return(string);
938 }
939
940 /* Returns an array of pids */
941 MonoArray *ves_icall_System_Diagnostics_Process_GetProcesses_internal (void)
942 {
943         MonoArray *procs;
944         gboolean ret;
945         guint32 needed, count, i;
946         guint32 pids[1024];
947
948         MONO_ARCH_SAVE_REGS;
949
950         ret=EnumProcesses (pids, sizeof(pids), &needed);
951         if(ret==FALSE) {
952                 /* FIXME: throw an exception */
953                 return(NULL);
954         }
955         
956         count=needed/sizeof(guint32);
957         procs=mono_array_new (mono_domain_get (), mono_defaults.int_class,
958                               count);
959         for(i=0; i<count; i++) {
960                 mono_array_set (procs, guint32, i, pids[i]);
961         }
962         
963         return(procs);
964 }
965
966 MonoBoolean ves_icall_System_Diagnostics_Process_GetWorkingSet_internal (HANDLE process, guint32 *min, guint32 *max)
967 {
968         gboolean ret;
969         size_t ws_min, ws_max;
970         
971         MONO_ARCH_SAVE_REGS;
972
973         ret=GetProcessWorkingSetSize (process, &ws_min, &ws_max);
974         *min=(guint32)ws_min;
975         *max=(guint32)ws_max;
976         
977         return(ret);
978 }
979
980 MonoBoolean ves_icall_System_Diagnostics_Process_SetWorkingSet_internal (HANDLE process, guint32 min, guint32 max, MonoBoolean use_min)
981 {
982         gboolean ret;
983         size_t ws_min;
984         size_t ws_max;
985         
986         MONO_ARCH_SAVE_REGS;
987
988         ret=GetProcessWorkingSetSize (process, &ws_min, &ws_max);
989         if(ret==FALSE) {
990                 return(FALSE);
991         }
992         
993         if(use_min==TRUE) {
994                 ws_min=min;
995         } else {
996                 ws_max=max;
997         }
998         
999         ret=SetProcessWorkingSetSize (process, ws_min, ws_max);
1000
1001         return(ret);
1002 }
1003
1004 MonoBoolean
1005 ves_icall_System_Diagnostics_Process_Kill_internal (HANDLE process, gint32 sig)
1006 {
1007         MONO_ARCH_SAVE_REGS;
1008
1009         /* sig == 1 -> Kill, sig == 2 -> CloseMainWindow */
1010
1011         return TerminateProcess (process, -sig);
1012 }
1013