X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmetadata%2Fprocess.c;h=0820433da78723817944eb2e77ca1f13fffc7b4b;hb=aa8abd1f58edd74206c26094543b0da55a941db3;hp=23ec4676e56245c3e5c6ded880182ff7690691fa;hpb=3d693eeb90339833968d66c3dc9fde2fa3ba2cef;p=mono.git diff --git a/mono/metadata/process.c b/mono/metadata/process.c index 23ec4676e56..0820433da78 100644 --- a/mono/metadata/process.c +++ b/mono/metadata/process.c @@ -4,8 +4,8 @@ * Author: * Dick Porter (dick@ximian.com) * - * (C) 2002 Ximian, Inc. - * Copyright (c) 2002-2006 Novell, Inc. + * Copyright 2002 Ximian, Inc. + * Copyright 2002-2006 Novell, Inc. */ #include @@ -13,7 +13,7 @@ #include #include -#include +#include #include #include #include @@ -21,11 +21,28 @@ #include #include #include +#include #include +#if defined (MINGW_CROSS_COMPILE) && defined (HAVE_GETPROCESSID) +#undef HAVE_GETPROCESSID +#endif +#ifndef HAVE_GETPROCESSID +#if defined(_MSC_VER) || defined(HAVE_WINTERNL_H) +#include +#ifndef NT_SUCCESS +#define NT_SUCCESS(status) ((NTSTATUS) (status) >= 0) +#endif /* !NT_SUCCESS */ +#else /* ! (defined(_MSC_VER) || defined(HAVE_WINTERNL_H)) */ +#include +#include +#endif /* (defined(_MSC_VER) || defined(HAVE_WINTERNL_H)) */ +#endif /* !HAVE_GETPROCESSID */ /* FIXME: fix this code to not depend so much on the inetrnals */ #include -#undef DEBUG +#define LOGDEBUG(...) +/* define LOGDEBUG(...) g_message(__VA_ARGS__) */ + HANDLE ves_icall_System_Diagnostics_Process_GetProcess_internal (guint32 pid) { @@ -59,11 +76,14 @@ void ves_icall_System_Diagnostics_Process_Process_free_internal (MonoObject *thi MONO_ARCH_SAVE_REGS; #ifdef THREAD_DEBUG - g_message (G_GNUC_PRETTY_FUNCTION ": Closing process %p, handle %p", - this, process); + g_message ("%s: Closing process %p, handle %p", __func__, this, process); #endif +#if defined(TARGET_WIN32) || defined(HOST_WIN32) CloseHandle (process); +#else + CloseProcess (process); +#endif } #define STASH_SYS_ASS(this) \ @@ -85,49 +105,16 @@ static guint32 unicode_chars (const gunichar2 *str) } while(1); } -static guint32 unicode_bytes (const gunichar2 *str) -{ - guint32 len=0; - - do { - if(str[len]=='\0') { - /* Include the terminators */ - return((len*2)+2); - } - len++; - } while(1); -} - -/* - * compare a null-terminated utf16 string and a normal string. - * Can be used only for ascii or latin1 chars. - */ -static gboolean -unicode_string_equals (const gunichar2 *str1, const gchar *str2) -{ - while (*str1 && *str2) { - if (*str1 != *str2) - return FALSE; - ++str1; - ++str2; - } - return *str1 == *str2; -} - static void process_set_field_object (MonoObject *obj, const gchar *fieldname, MonoObject *data) { MonoClassField *field; -#ifdef DEBUG - g_message (G_GNUC_PRETTY_FUNCTION ": Setting field %s to object at %p", - fieldname, data); -#endif + LOGDEBUG (g_message ("%s: Setting field %s to object at %p", __func__, fieldname, data)); field=mono_class_get_field_from_name (mono_object_class (obj), fieldname); - /* FIXME: moving GC */ - *(MonoObject **)(((char *)obj) + field->offset)=data; + mono_gc_wbarrier_generic_store (((char *)obj) + field->offset, data); } static void process_set_field_string (MonoObject *obj, const gchar *fieldname, @@ -136,52 +123,37 @@ static void process_set_field_string (MonoObject *obj, const gchar *fieldname, MonoClassField *field; MonoString *string; -#ifdef DEBUG - g_message (G_GNUC_PRETTY_FUNCTION ": Setting field %s to [%s]", - fieldname, g_utf16_to_utf8 (val, len, NULL, NULL, NULL)); -#endif + LOGDEBUG (g_message ("%s: Setting field %s to [%s]", __func__, fieldname, g_utf16_to_utf8 (val, len, NULL, NULL, NULL))); string=mono_string_new_utf16 (mono_object_domain (obj), val, len); field=mono_class_get_field_from_name (mono_object_class (obj), fieldname); - /* FIXME: moving GC */ - *(MonoString **)(((char *)obj) + field->offset)=string; + mono_gc_wbarrier_generic_store (((char *)obj) + field->offset, (MonoObject*)string); } -static void process_set_field_string_utf8 (MonoObject *obj, - const gchar *fieldname, - const gchar *val) +static void process_set_field_int (MonoObject *obj, const gchar *fieldname, + guint32 val) { MonoClassField *field; - MonoString *string; - -#ifdef DEBUG - g_message (G_GNUC_PRETTY_FUNCTION ": Setting field %s to [%s]", - fieldname, val); -#endif - string=mono_string_new (mono_object_domain (obj), val); + LOGDEBUG (g_message ("%s: Setting field %s to %d", __func__,fieldname, val)); field=mono_class_get_field_from_name (mono_object_class (obj), fieldname); - /* FIXME: moving GC */ - *(MonoString **)(((char *)obj) + field->offset)=string; + *(guint32 *)(((char *)obj) + field->offset)=val; } -static void process_set_field_int (MonoObject *obj, const gchar *fieldname, - guint32 val) +static void process_set_field_intptr (MonoObject *obj, const gchar *fieldname, + gpointer val) { MonoClassField *field; -#ifdef DEBUG - g_message (G_GNUC_PRETTY_FUNCTION ": Setting field %s to %d", - fieldname, val); -#endif + LOGDEBUG (g_message ("%s: Setting field %s to %p", __func__, fieldname, val)); field=mono_class_get_field_from_name (mono_object_class (obj), fieldname); - *(guint32 *)(((char *)obj) + field->offset)=val; + *(gpointer *)(((char *)obj) + field->offset)=val; } static void process_set_field_bool (MonoObject *obj, const gchar *fieldname, @@ -189,383 +161,223 @@ static void process_set_field_bool (MonoObject *obj, const gchar *fieldname, { MonoClassField *field; -#ifdef DEBUG - g_message (G_GNUC_PRETTY_FUNCTION ": Setting field %s to %s", - fieldname, val?"TRUE":"FALSE"); -#endif + LOGDEBUG (g_message ("%s: Setting field %s to %s", __func__, fieldname, val?"TRUE":"FALSE")); field=mono_class_get_field_from_name (mono_object_class (obj), fieldname); *(guint8 *)(((char *)obj) + field->offset)=val; } -typedef struct { - guint16 data_len; - guint16 value_len; - guint16 type; - gunichar2 *key; -} version_data; - -/* Returns a pointer to the value data, because theres no way to know - * how big that data is (value_len is set to zero for most blocks :-() - */ -static gpointer process_get_versioninfo_block (gpointer data, - version_data *block) +#define SFI_COMMENTS "\\StringFileInfo\\%02X%02X%02X%02X\\Comments" +#define SFI_COMPANYNAME "\\StringFileInfo\\%02X%02X%02X%02X\\CompanyName" +#define SFI_FILEDESCRIPTION "\\StringFileInfo\\%02X%02X%02X%02X\\FileDescription" +#define SFI_FILEVERSION "\\StringFileInfo\\%02X%02X%02X%02X\\FileVersion" +#define SFI_INTERNALNAME "\\StringFileInfo\\%02X%02X%02X%02X\\InternalName" +#define SFI_LEGALCOPYRIGHT "\\StringFileInfo\\%02X%02X%02X%02X\\LegalCopyright" +#define SFI_LEGALTRADEMARKS "\\StringFileInfo\\%02X%02X%02X%02X\\LegalTrademarks" +#define SFI_ORIGINALFILENAME "\\StringFileInfo\\%02X%02X%02X%02X\\OriginalFilename" +#define SFI_PRIVATEBUILD "\\StringFileInfo\\%02X%02X%02X%02X\\PrivateBuild" +#define SFI_PRODUCTNAME "\\StringFileInfo\\%02X%02X%02X%02X\\ProductName" +#define SFI_PRODUCTVERSION "\\StringFileInfo\\%02X%02X%02X%02X\\ProductVersion" +#define SFI_SPECIALBUILD "\\StringFileInfo\\%02X%02X%02X%02X\\SpecialBuild" +#define EMPTY_STRING (gunichar2*)"\000\000" + +static void process_module_string_read (MonoObject *filever, gpointer data, + const gchar *fieldname, + guchar lang_hi, guchar lang_lo, + const gchar *key) { - block->data_len=*((guint16 *)data); - data = (char *)data + sizeof(guint16); - block->value_len=*((guint16 *)data); - data = (char *)data + sizeof(guint16); - - /* No idea what the type is supposed to indicate */ - block->type=*((guint16 *)data); - data = (char *)data + sizeof(guint16); - block->key=((gunichar2 *)data); - - /* skip over the key (including the terminator) */ - data=((gunichar2 *)data)+(unicode_chars (block->key)+1); - - /* align on a 32-bit boundary */ - data=(gpointer)((char *)data + 3); - data=(gpointer)((char *)data - (GPOINTER_TO_INT(data) & 3)); - - return(data); -} + gchar *lang_key_utf8; + gunichar2 *lang_key, *buffer; + UINT chars; -/* Returns a pointer to the byte following the Var block */ -static gpointer process_read_var_block (MonoObject *filever, gpointer data_ptr, - guint16 data_len) -{ - /* Not currently interested in the VarFileInfo block. This - * might change if language support is needed for file version - * strings (VarFileInfo contains lists of supported - * languages.) - */ - version_data block; + lang_key_utf8 = g_strdup_printf (key, lang_lo, lang_hi, 0x04, 0xb0); - /* data_ptr is pointing at a Var block of length data_len */ - data_ptr=process_get_versioninfo_block (data_ptr, &block); - data_ptr=((guchar *)data_ptr)+block.value_len; + LOGDEBUG (g_message ("%s: asking for [%s]", __func__, lang_key_utf8)); - return(data_ptr); -} + lang_key = g_utf8_to_utf16 (lang_key_utf8, -1, NULL, NULL, NULL); -/* Returns a pointer to the byte following the String block, or NULL - * if the data read hits padding. We can't recover from this because - * the data length does not include padding bytes, so it's not - * possible to just return the start position + length. - */ -static gpointer process_read_string_block (MonoObject *filever, - gpointer data_ptr, - guint16 data_len, - gboolean store) -{ - version_data block; - guint16 string_len=28; /* Length of the StringTable block */ - - /* data_ptr is pointing at an array of one or more String - * blocks with total length (not including alignment padding) - * of data_len. - */ - while(string_len 0) { + LOGDEBUG (g_message ("%s: found %d chars of [%s]", __func__, chars, g_utf16_to_utf8 (buffer, chars, NULL, NULL, NULL))); + /* chars includes trailing null */ + process_set_field_string (filever, fieldname, buffer, chars - 1); + } else { + process_set_field_string (filever, fieldname, EMPTY_STRING, 0); } - - return(data_ptr); -} - -/* returns a pointer to the byte following the Stringtable block, or - * NULL if the data read hits padding. We can't recover from this - * because the data length does not include padding bytes, so it's not - * possible to just return the start position + length - */ -static gpointer process_read_stringtable_block (MonoObject *filever, - gpointer data_ptr, - guint16 data_len) -{ - version_data block; - gchar *language; - guint16 string_len=36; /* length of the StringFileInfo block */ - - /* data_ptr is pointing at an array of StringTable blocks, - * with total length (not including alignment padding) of - * data_len. - */ - - while(string_lendwSignature, ffi->dwStrucVersion, ffi->dwFileVersionMS, ffi->dwFileVersionLS, ffi->dwProductVersionMS, ffi->dwProductVersionLS, ffi->dwFileFlagsMask, ffi->dwFileFlags, ffi->dwFileOS, ffi->dwFileType, ffi->dwFileSubtype, ffi->dwFileDateMS, ffi->dwFileDateLS); -#endif - - process_set_field_int (filever, "filemajorpart", - HIWORD (ffi->dwFileVersionMS)); - process_set_field_int (filever, "fileminorpart", - LOWORD (ffi->dwFileVersionMS)); - process_set_field_int (filever, "filebuildpart", - HIWORD (ffi->dwFileVersionLS)); - process_set_field_int (filever, "fileprivatepart", - LOWORD (ffi->dwFileVersionLS)); - - process_set_field_int (filever, "productmajorpart", - HIWORD (ffi->dwProductVersionMS)); - process_set_field_int (filever, "productminorpart", - LOWORD (ffi->dwProductVersionMS)); - process_set_field_int (filever, "productbuildpart", - HIWORD (ffi->dwProductVersionLS)); - process_set_field_int (filever, "productprivatepart", - LOWORD (ffi->dwProductVersionLS)); - - process_set_field_bool (filever, "isdebug", - ffi->dwFileFlags&VS_FF_DEBUG); - process_set_field_bool (filever, "isprerelease", - ffi->dwFileFlags&VS_FF_PRERELEASE); - process_set_field_bool (filever, "ispatched", - ffi->dwFileFlags&VS_FF_PATCHED); - process_set_field_bool (filever, "isprivatebuild", - ffi->dwFileFlags&VS_FF_PRIVATEBUILD); - process_set_field_bool (filever, "isspecialbuild", - ffi->dwFileFlags&VS_FF_SPECIALBUILD); + process_module_string_read (filever, data, "comments", lang_hi, lang_lo, + SFI_COMMENTS); + process_module_string_read (filever, data, "companyname", lang_hi, + lang_lo, SFI_COMPANYNAME); + process_module_string_read (filever, data, "filedescription", lang_hi, + lang_lo, SFI_FILEDESCRIPTION); + process_module_string_read (filever, data, "fileversion", lang_hi, + lang_lo, SFI_FILEVERSION); + process_module_string_read (filever, data, "internalname", lang_hi, + lang_lo, SFI_INTERNALNAME); + process_module_string_read (filever, data, "legalcopyright", lang_hi, + lang_lo, SFI_LEGALCOPYRIGHT); + process_module_string_read (filever, data, "legaltrademarks", lang_hi, + lang_lo, SFI_LEGALTRADEMARKS); + process_module_string_read (filever, data, "originalfilename", lang_hi, + lang_lo, SFI_ORIGINALFILENAME); + process_module_string_read (filever, data, "privatebuild", lang_hi, + lang_lo, SFI_PRIVATEBUILD); + process_module_string_read (filever, data, "productname", lang_hi, + lang_lo, SFI_PRODUCTNAME); + process_module_string_read (filever, data, "productversion", lang_hi, + lang_lo, SFI_PRODUCTVERSION); + process_module_string_read (filever, data, "specialbuild", lang_hi, + lang_lo, SFI_SPECIALBUILD); } -static void process_get_fileversion (MonoObject *filever, MonoImage *image) +static void process_get_fileversion (MonoObject *filever, gunichar2 *filename) { - MonoPEResourceDataEntry *version_info; - gpointer data; + DWORD verinfohandle; VS_FIXEDFILEINFO *ffi; - gpointer data_ptr; - version_data block; - gint32 data_len; /* signed to guard against underflow */ - - version_info=mono_image_lookup_resource (image, - MONO_PE_RESOURCE_ID_VERSION, - 0, NULL); -#ifdef DEBUG - g_message (G_GNUC_PRETTY_FUNCTION ": image_lookup returned %p", - version_info); -#endif - - if(version_info==NULL) { - return; - } - - data=mono_image_rva_map (image, - version_info->rde_data_offset); - if(data==NULL) { - return; - } - - /* See io-layer/versioninfo.h for the gory details on how this - * data is laid out. (data should be pointing to - * VS_VERSIONINFO data). - */ - - data_ptr=process_get_versioninfo_block (data, &block); - - data_len=block.data_len; - - if(block.value_len!=sizeof(VS_FIXEDFILEINFO)) { -#ifdef DEBUG - g_message (G_GNUC_PRETTY_FUNCTION - ": FIXEDFILEINFO size mismatch"); -#endif - return; - } - - if (!unicode_string_equals (block.key, "VS_VERSION_INFO")) { -#ifdef DEBUG - g_message (G_GNUC_PRETTY_FUNCTION - ": VS_VERSION_INFO mismatch"); -#endif - return; - } - - ffi=((VS_FIXEDFILEINFO *)data_ptr); - data_ptr = (char *)data_ptr + sizeof(VS_FIXEDFILEINFO); - if((ffi->dwSignature!=VS_FFI_SIGNATURE) || - (ffi->dwStrucVersion!=VS_FFI_STRUCVERSION)) { -#ifdef DEBUG - g_message (G_GNUC_PRETTY_FUNCTION - ": FIXEDFILEINFO bad signature"); -#endif - return; - } - process_read_fixedfileinfo_block (filever, ffi); - - /* Subtract the 92 bytes we've already seen */ - data_len -= 92; + gpointer data; + DWORD datalen; + guchar *trans_data; + gunichar2 *query; + UINT ffi_size, trans_size; + BOOL ok; + gunichar2 lang_buf[128]; + guint32 lang, lang_count; + + datalen = GetFileVersionInfoSize (filename, &verinfohandle); + if (datalen) { + data = g_malloc0 (datalen); + ok = GetFileVersionInfo (filename, verinfohandle, datalen, + data); + if (ok) { + query = g_utf8_to_utf16 ("\\", -1, NULL, NULL, NULL); + if (query == NULL) { + g_free (data); + return; + } + + if (VerQueryValue (data, query, (gpointer *)&ffi, + &ffi_size)) { + LOGDEBUG (g_message ("%s: recording assembly: FileName [%s] FileVersionInfo [%d.%d.%d.%d]", __func__, g_utf16_to_utf8 (filename, -1, NULL, NULL, NULL), HIWORD (ffi->dwFileVersionMS), LOWORD (ffi->dwFileVersionMS), HIWORD (ffi->dwFileVersionLS), LOWORD (ffi->dwFileVersionLS))); - /* There now follow zero or one StringFileInfo blocks and zero - * or one VarFileInfo blocks - */ - while(data_len > 0) { - /* align on a 32-bit boundary */ - data_ptr=(gpointer)((char *)data_ptr + 3); - data_ptr=(gpointer)((char *)data_ptr - - (GPOINTER_TO_INT(data_ptr) & 3)); - - data_ptr=process_get_versioninfo_block (data_ptr, &block); - if(block.data_len==0) { - /* We must have hit padding, so give up - * processing now - */ -#ifdef DEBUG - g_message (G_GNUC_PRETTY_FUNCTION - ": Hit 0-length block, giving up"); -#endif - return; - } - - data_len=data_len-block.data_len; - - if (unicode_string_equals (block.key, "VarFileInfo")) { - data_ptr=process_read_var_block (filever, data_ptr, - block.data_len); - } else if (unicode_string_equals (block.key, "StringFileInfo")) { - data_ptr=process_read_stringtable_block (filever, data_ptr, block.data_len); - } else { - /* Bogus data */ -#ifdef DEBUG - g_message (G_GNUC_PRETTY_FUNCTION - ": Not a valid VERSIONINFO child block"); - return; -#endif - } + process_set_field_int (filever, "filemajorpart", HIWORD (ffi->dwFileVersionMS)); + process_set_field_int (filever, "fileminorpart", LOWORD (ffi->dwFileVersionMS)); + process_set_field_int (filever, "filebuildpart", HIWORD (ffi->dwFileVersionLS)); + process_set_field_int (filever, "fileprivatepart", LOWORD (ffi->dwFileVersionLS)); + + process_set_field_int (filever, "productmajorpart", HIWORD (ffi->dwProductVersionMS)); + process_set_field_int (filever, "productminorpart", LOWORD (ffi->dwProductVersionMS)); + process_set_field_int (filever, "productbuildpart", HIWORD (ffi->dwProductVersionLS)); + process_set_field_int (filever, "productprivatepart", LOWORD (ffi->dwProductVersionLS)); + + process_set_field_bool (filever, "isdebug", (ffi->dwFileFlags & ffi->dwFileFlagsMask) & VS_FF_DEBUG); + process_set_field_bool (filever, "isprerelease", (ffi->dwFileFlags & ffi->dwFileFlagsMask) & VS_FF_PRERELEASE); + process_set_field_bool (filever, "ispatched", (ffi->dwFileFlags & ffi->dwFileFlagsMask) & VS_FF_PATCHED); + process_set_field_bool (filever, "isprivatebuild", (ffi->dwFileFlags & ffi->dwFileFlagsMask) & VS_FF_PRIVATEBUILD); + process_set_field_bool (filever, "isspecialbuild", (ffi->dwFileFlags & ffi->dwFileFlagsMask) & VS_FF_SPECIALBUILD); + } + g_free (query); - if(data_ptr==NULL) { - /* Child block hit padding */ -#ifdef DEBUG - g_message (G_GNUC_PRETTY_FUNCTION ": Child block hit 0-length block, giving up"); -#endif - return; + query = g_utf8_to_utf16 ("\\VarFileInfo\\Translation", -1, NULL, NULL, NULL); + if (query == NULL) { + g_free (data); + return; + } + + if (VerQueryValue (data, query, + (gpointer *)&trans_data, + &trans_size)) { + /* use the first language ID we see + */ + if (trans_size >= 4) { + LOGDEBUG (g_message("%s: %s has 0x%0x 0x%0x 0x%0x 0x%0x", __func__, g_utf16_to_utf8 (filename, -1, NULL, NULL, NULL), trans_data[0], trans_data[1], trans_data[2], trans_data[3])); + lang = (trans_data[0]) | + (trans_data[1] << 8) | + (trans_data[2] << 16) | + (trans_data[3] << 24); + /* Only give the lower 16 bits + * to VerLanguageName, as + * Windows gets confused + * otherwise + */ + lang_count = VerLanguageName (lang & 0xFFFF, lang_buf, 128); + if (lang_count) { + process_set_field_string (filever, "language", lang_buf, lang_count); + } + process_module_stringtable (filever, data, trans_data[0], trans_data[1]); + } + } else { + /* No strings, so set every field to + * the empty string + */ + process_set_field_string (filever, + "comments", + EMPTY_STRING, 0); + process_set_field_string (filever, + "companyname", + EMPTY_STRING, 0); + process_set_field_string (filever, + "filedescription", + EMPTY_STRING, 0); + process_set_field_string (filever, + "fileversion", + EMPTY_STRING, 0); + process_set_field_string (filever, + "internalname", + EMPTY_STRING, 0); + process_set_field_string (filever, + "legalcopyright", + EMPTY_STRING, 0); + process_set_field_string (filever, + "legaltrademarks", + EMPTY_STRING, 0); + process_set_field_string (filever, + "originalfilename", + EMPTY_STRING, 0); + process_set_field_string (filever, + "privatebuild", + EMPTY_STRING, 0); + process_set_field_string (filever, + "productname", + EMPTY_STRING, 0); + process_set_field_string (filever, + "productversion", + EMPTY_STRING, 0); + process_set_field_string (filever, + "specialbuild", + EMPTY_STRING, 0); + + /* And language seems to be set to + * en_US according to bug 374600 + */ + lang_count = VerLanguageName (0x0409, lang_buf, 128); + if (lang_count) { + process_set_field_string (filever, "language", lang_buf, lang_count); + } + } + + g_free (query); } + g_free (data); } } -static void process_add_module (GPtrArray *modules, MonoAssembly *ass) +static MonoObject* process_add_module (HANDLE process, HMODULE mod, gunichar2 *filename, gunichar2 *modulename) { MonoClass *proc_class, *filever_class; MonoObject *item, *filever; MonoDomain *domain=mono_domain_get (); - gchar *modulename; - const char* filename; + MODULEINFO modinfo; + BOOL ok; /* Build a System.Diagnostics.ProcessModule with the data. - * Leave BaseAddress and EntryPointAddress set to NULL, - * FileName is ass->image->name, FileVersionInfo is an object - * constructed from the PE image data referenced by - * ass->image, ModuleMemorySize set to 0, ModuleName the last - * component of FileName. */ proc_class=mono_class_from_name (system_assembly, "System.Diagnostics", "ProcessModule"); @@ -575,100 +387,80 @@ static void process_add_module (GPtrArray *modules, MonoAssembly *ass) "System.Diagnostics", "FileVersionInfo"); filever=mono_object_new (domain, filever_class); - -#ifdef DEBUG - 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); -#endif - - process_get_fileversion (filever, mono_assembly_get_image (ass)); - - filename = mono_image_get_filename (mono_assembly_get_image (ass)); - process_set_field_string_utf8 (filever, "filename", filename); - process_set_field_string_utf8 (item, "filename", filename); - process_set_field_object (item, "version_info", filever); - - modulename=g_path_get_basename (filename); - process_set_field_string_utf8 (item, "modulename", modulename); - g_free (modulename); - /* FIXME: moving GC */ - g_ptr_array_add (modules, item); -} + process_get_fileversion (filever, filename); -static void process_scan_modules (gpointer data, gpointer user_data) -{ - MonoAssembly *ass=data; - GPtrArray *modules=user_data; + process_set_field_string (filever, "filename", filename, + unicode_chars (filename)); - /* The main assembly is already in the list */ - if(mono_assembly_get_main () != ass) { - process_add_module (modules, ass); + ok = GetModuleInformation (process, mod, &modinfo, sizeof(MODULEINFO)); + if (ok) { + process_set_field_intptr (item, "baseaddr", + modinfo.lpBaseOfDll); + process_set_field_intptr (item, "entryaddr", + modinfo.EntryPoint); + process_set_field_int (item, "memory_size", + modinfo.SizeOfImage); } -} + process_set_field_string (item, "filename", filename, + unicode_chars (filename)); + process_set_field_string (item, "modulename", modulename, + unicode_chars (modulename)); + process_set_field_object (item, "version_info", filever); + return item; +} /* Returns an array of System.Diagnostics.ProcessModule */ -MonoArray *ves_icall_System_Diagnostics_Process_GetModules_internal (MonoObject *this) +MonoArray *ves_icall_System_Diagnostics_Process_GetModules_internal (MonoObject *this, HANDLE process) { - /* I was going to use toolhelp for this, but then realised I - * was being an idiot :) - * - * (Toolhelp would give shared libraries open by the runtime, - * as well as open assemblies. On windows my tests didnt find - * the assemblies loaded by mono either.) - */ - GPtrArray *modules_list=g_ptr_array_new (); + MonoArray *temp_arr = NULL; MonoArray *arr; - guint32 i; - - MONO_ARCH_SAVE_REGS; + HMODULE mods[1024]; + gunichar2 filename[MAX_PATH]; + gunichar2 modname[MAX_PATH]; + DWORD needed; + guint32 count = 0; + guint32 i, num_added = 0; STASH_SYS_ASS (this); - - /* Make sure the first entry is the main module */ - process_add_module (modules_list, mono_assembly_get_main ()); - - mono_assembly_foreach (process_scan_modules, modules_list); - /* Build a MonoArray out of modules_list */ - arr=mono_array_new (mono_domain_get (), mono_get_object_class (), - modules_list->len); - - for(i=0; ilen; i++) { - mono_array_setref (arr, i, g_ptr_array_index (modules_list, i)); + if (EnumProcessModules (process, mods, sizeof(mods), &needed)) { + count = needed / sizeof(HMODULE); + temp_arr = mono_array_new (mono_domain_get (), mono_get_object_class (), count); + for (i = 0; i < count; i++) { + if (GetModuleBaseName (process, mods[i], modname, MAX_PATH) && + GetModuleFileNameEx (process, mods[i], filename, MAX_PATH)) { + MonoObject *module = process_add_module (process, mods[i], + filename, modname); + mono_array_setref (temp_arr, num_added++, module); + } + } } - - g_ptr_array_free (modules_list, TRUE); - - return(arr); + + if (count == num_added) { + arr = temp_arr; + } else { + /* shorter version of the array */ + arr = mono_array_new (mono_domain_get (), mono_get_object_class (), num_added); + + for (i = 0; i < num_added; i++) + mono_array_setref (arr, i, mono_array_get (temp_arr, MonoObject*, i)); + } + + return arr; } void ves_icall_System_Diagnostics_FileVersionInfo_GetVersionInfo_internal (MonoObject *this, MonoString *filename) { - MonoImage *image; - gchar *filename_utf8; - MONO_ARCH_SAVE_REGS; STASH_SYS_ASS (this); - filename_utf8 = mono_string_to_utf8 (filename); - image = mono_pe_file_open (filename_utf8, NULL); - g_free (filename_utf8); - - if(image==NULL) { - /* FIXME: an exception might be appropriate here */ -#ifdef DEBUG - g_message (G_GNUC_PRETTY_FUNCTION ": Failed to load image"); -#endif - - return; - } - - process_get_fileversion (this, image); - process_set_field_string_utf8 (this, "filename", mono_image_get_filename (image)); - - mono_image_close (image); + process_get_fileversion (this, mono_string_chars (filename)); + process_set_field_string (this, "filename", + mono_string_chars (filename), + mono_string_length (filename)); } /* Only used when UseShellExecute is false */ @@ -676,7 +468,7 @@ static gchar * quote_path (const gchar *path) { gchar *res = g_shell_quote (path); -#ifdef PLATFORM_WIN32 +#ifdef TARGET_WIN32 { gchar *q = res; while (*q) { @@ -693,35 +485,119 @@ quote_path (const gchar *path) static gboolean complete_path (const gunichar2 *appname, gchar **completed) { - gchar *utf8app; + gchar *utf8app, *utf8appmemory; gchar *found; - utf8app = g_utf16_to_utf8 (appname, -1, NULL, NULL, NULL); + utf8appmemory = utf8app = g_utf16_to_utf8 (appname, -1, NULL, NULL, NULL); +#ifdef TARGET_WIN32 // Should this happen on all platforms? + { + // remove the quotes around utf8app. + size_t len; + len = strlen (utf8app); + if (len) { + if (utf8app[len-1] == '\"') + utf8app[len-1] = '\0'; + if (utf8app[0] == '\"') + utf8app++; + } + } +#endif + if (g_path_is_absolute (utf8app)) { *completed = quote_path (utf8app); - g_free (utf8app); + g_free (utf8appmemory); return TRUE; } if (g_file_test (utf8app, G_FILE_TEST_IS_EXECUTABLE) && !g_file_test (utf8app, G_FILE_TEST_IS_DIR)) { *completed = quote_path (utf8app); - g_free (utf8app); + g_free (utf8appmemory); return TRUE; } found = g_find_program_in_path (utf8app); if (found == NULL) { *completed = NULL; - g_free (utf8app); + g_free (utf8appmemory); return FALSE; } *completed = quote_path (found); g_free (found); - g_free (utf8app); + g_free (utf8appmemory); return TRUE; } +#ifndef HAVE_GETPROCESSID +/* Run-time GetProcessId detection for Windows */ +#ifdef TARGET_WIN32 +#define HAVE_GETPROCESSID + +typedef DWORD (WINAPI *GETPROCESSID_PROC) (HANDLE); +typedef DWORD (WINAPI *NTQUERYINFORMATIONPROCESS_PROC) (HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG); +typedef DWORD (WINAPI *RTLNTSTATUSTODOSERROR_PROC) (NTSTATUS); + +static DWORD WINAPI GetProcessId_detect (HANDLE process); + +static GETPROCESSID_PROC GetProcessId = &GetProcessId_detect; +static NTQUERYINFORMATIONPROCESS_PROC NtQueryInformationProcess_proc = NULL; +static RTLNTSTATUSTODOSERROR_PROC RtlNtStatusToDosError_proc = NULL; + +static DWORD WINAPI GetProcessId_ntdll (HANDLE process) +{ + PROCESS_BASIC_INFORMATION pi; + NTSTATUS status; + + status = NtQueryInformationProcess_proc (process, ProcessBasicInformation, &pi, sizeof (pi), NULL); + if (NT_SUCCESS (status)) { + return pi.UniqueProcessId; + } else { + SetLastError (RtlNtStatusToDosError_proc (status)); + return 0; + } +} + +static DWORD WINAPI GetProcessId_stub (HANDLE process) +{ + SetLastError (ERROR_CALL_NOT_IMPLEMENTED); + return 0; +} + +static DWORD WINAPI GetProcessId_detect (HANDLE process) +{ + HMODULE module_handle; + GETPROCESSID_PROC GetProcessId_kernel; + + /* Windows XP SP1 and above have GetProcessId API */ + module_handle = GetModuleHandle (L"kernel32.dll"); + if (module_handle != NULL) { + GetProcessId_kernel = (GETPROCESSID_PROC) GetProcAddress (module_handle, "GetProcessId"); + if (GetProcessId_kernel != NULL) { + GetProcessId = GetProcessId_kernel; + return GetProcessId (process); + } + } + + /* Windows 2000 and above have deprecated NtQueryInformationProcess API */ + module_handle = GetModuleHandle (L"ntdll.dll"); + if (module_handle != NULL) { + NtQueryInformationProcess_proc = (NTQUERYINFORMATIONPROCESS_PROC) GetProcAddress (module_handle, "NtQueryInformationProcess"); + if (NtQueryInformationProcess_proc != NULL) { + RtlNtStatusToDosError_proc = (RTLNTSTATUSTODOSERROR_PROC) GetProcAddress (module_handle, "RtlNtStatusToDosError"); + if (RtlNtStatusToDosError_proc != NULL) { + GetProcessId = &GetProcessId_ntdll; + return GetProcessId (process); + } + } + } + + /* Fall back to ERROR_CALL_NOT_IMPLEMENTED */ + GetProcessId = &GetProcessId_stub; + return GetProcessId (process); +} +#endif /* HOST_WIN32 */ +#endif /* !HAVE_GETPROCESSID */ + MonoBoolean ves_icall_System_Diagnostics_Process_ShellExecuteEx_internal (MonoProcessStartInfo *proc_start_info, MonoProcInfo *process_info) { SHELLEXECUTEINFO shellex = {0}; @@ -729,7 +605,9 @@ MonoBoolean ves_icall_System_Diagnostics_Process_ShellExecuteEx_internal (MonoPr shellex.cbSize = sizeof(SHELLEXECUTEINFO); shellex.fMask = SEE_MASK_FLAG_DDEWAIT | SEE_MASK_NOCLOSEPROCESS | SEE_MASK_UNICODE; - shellex.nShow = SW_SHOWNORMAL; + shellex.nShow = proc_start_info->window_style; + shellex.nShow = (shellex.nShow == 0) ? 1 : (shellex.nShow == 1 ? 0 : shellex.nShow); + if (proc_start_info->filename != NULL) { shellex.lpFile = mono_string_chars (proc_start_info->filename); @@ -749,6 +627,12 @@ MonoBoolean ves_icall_System_Diagnostics_Process_ShellExecuteEx_internal (MonoPr shellex.lpDirectory = mono_string_chars (proc_start_info->working_directory); } + if (proc_start_info->error_dialog) { + shellex.hwnd = proc_start_info->error_dialog_parent_handle; + } else { + shellex.fMask |= SEE_MASK_FLAG_NO_UI; + } + ret = ShellExecuteEx (&shellex); if (ret == FALSE) { process_info->pid = -GetLastError (); @@ -758,7 +642,7 @@ MonoBoolean ves_icall_System_Diagnostics_Process_ShellExecuteEx_internal (MonoPr /* It appears that there's no way to get the pid from a * process handle before windows xp. Really. */ -#ifdef HAVE_GETPROCESSID +#if defined(HAVE_GETPROCESSID) && !defined(MONO_CROSS_COMPILE) process_info->pid = GetProcessId (shellex.hProcess); #else process_info->pid = 0; @@ -780,12 +664,17 @@ MonoBoolean ves_icall_System_Diagnostics_Process_CreateProcess_internal (MonoPro gboolean free_shell_path = TRUE; gchar *spath = NULL; MonoString *cmd = proc_start_info->arguments; + guint32 creation_flags, logon_flags; startinfo.cb=sizeof(STARTUPINFO); startinfo.dwFlags=STARTF_USESTDHANDLES; startinfo.hStdInput=stdin_handle; startinfo.hStdOutput=stdout_handle; startinfo.hStdError=stderr_handle; + + creation_flags = CREATE_UNICODE_ENVIRONMENT; + if (proc_start_info->create_no_window) + creation_flags |= CREATE_NO_WINDOW; shell_path = mono_string_chars (proc_start_info->filename); complete_path (shell_path, &spath); @@ -793,7 +682,7 @@ MonoBoolean ves_icall_System_Diagnostics_Process_CreateProcess_internal (MonoPro process_info->pid = -ERROR_FILE_NOT_FOUND; return FALSE; } -#ifdef PLATFORM_WIN32 +#ifdef TARGET_WIN32 /* Seems like our CreateProcess does not work as the windows one. * This hack is needed to deal with paths containing spaces */ shell_path = NULL; @@ -858,13 +747,18 @@ MonoBoolean ves_icall_System_Diagnostics_Process_CreateProcess_internal (MonoPro /* The default dir name is "". Turn that into NULL to mean * "current directory" */ - if(mono_string_length (proc_start_info->working_directory)==0) { + if(proc_start_info->working_directory == NULL || mono_string_length (proc_start_info->working_directory)==0) { dir=NULL; } else { dir=mono_string_chars (proc_start_info->working_directory); } - - ret=CreateProcess (shell_path, cmd? mono_string_chars (cmd): NULL, NULL, NULL, TRUE, CREATE_UNICODE_ENVIRONMENT, env_vars, dir, &startinfo, &procinfo); + + if (process_info->username) { + logon_flags = process_info->load_user_profile ? LOGON_WITH_PROFILE : 0; + ret=CreateProcessWithLogonW (mono_string_chars (process_info->username), process_info->domain ? mono_string_chars (process_info->domain) : NULL, process_info->password, logon_flags, shell_path, cmd? mono_string_chars (cmd): NULL, creation_flags, env_vars, dir, &startinfo, &procinfo); + } else { + ret=CreateProcess (shell_path, cmd? mono_string_chars (cmd): NULL, NULL, NULL, TRUE, creation_flags, env_vars, dir, &startinfo, &procinfo); + } g_free (env_vars); if (free_shell_path) @@ -904,6 +798,22 @@ MonoBoolean ves_icall_System_Diagnostics_Process_WaitForExit_internal (MonoObjec } } +MonoBoolean ves_icall_System_Diagnostics_Process_WaitForInputIdle_internal (MonoObject *this, HANDLE process, gint32 ms) +{ + guint32 ret; + + MONO_ARCH_SAVE_REGS; + + if(ms<0) { + /* Wait forever */ + ret=WaitForInputIdle (process, INFINITE); + } else { + ret=WaitForInputIdle (process, ms); + } + + return (ret) ? FALSE : TRUE; +} + gint64 ves_icall_System_Diagnostics_Process_ExitTime_internal (HANDLE process) { gboolean ret; @@ -946,15 +856,13 @@ gint64 ves_icall_System_Diagnostics_Process_StartTime_internal (HANDLE process) gint32 ves_icall_System_Diagnostics_Process_ExitCode_internal (HANDLE process) { - guint32 code; + DWORD code; MONO_ARCH_SAVE_REGS; GetExitCodeProcess (process, &code); -#ifdef DEBUG - g_message (G_GNUC_PRETTY_FUNCTION ": process exit code is %d", code); -#endif + LOGDEBUG (g_message ("%s: process exit code is %d", __func__, code)); return(code); } @@ -965,7 +873,7 @@ MonoString *ves_icall_System_Diagnostics_Process_ProcessName_internal (HANDLE pr gboolean ok; HMODULE mod; gunichar2 name[MAX_PATH]; - guint32 needed; + DWORD needed; guint32 len; MONO_ARCH_SAVE_REGS; @@ -980,10 +888,7 @@ MonoString *ves_icall_System_Diagnostics_Process_ProcessName_internal (HANDLE pr return(NULL); } -#ifdef DEBUG - g_message (G_GNUC_PRETTY_FUNCTION ": process name is [%s]", - g_utf16_to_utf8 (name, -1, NULL, NULL, NULL)); -#endif + LOGDEBUG (g_message ("%s: process name is [%s]", __func__, g_utf16_to_utf8 (name, -1, NULL, NULL, NULL))); string=mono_string_new_utf16 (mono_domain_get (), name, len); @@ -995,31 +900,45 @@ MonoArray *ves_icall_System_Diagnostics_Process_GetProcesses_internal (void) { MonoArray *procs; gboolean ret; - guint32 needed, count, i; - guint32 pids[1024]; + DWORD needed; + guint32 count; + guint32 *pids; MONO_ARCH_SAVE_REGS; - ret=EnumProcesses (pids, sizeof(pids), &needed); - if(ret==FALSE) { - /* FIXME: throw an exception */ - return(NULL); - } - - count=needed/sizeof(guint32); - procs=mono_array_new (mono_domain_get (), mono_get_int32_class (), - count); - for(i=0; i