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