Tue Sep 11 16:08:08 CEST 2007 Paolo Molaro <lupus@ximian.com>
[mono.git] / mono / metadata / process.c
index 24074a49bba1471704ab9a4f052f2ac576cfbf77..1d08c7f481397d5a4175c83f1e4826f0cdc9bbdb 100644 (file)
@@ -5,6 +5,7 @@
  *     Dick Porter (dick@ximian.com)
  *
  * (C) 2002 Ximian, Inc.
+ * Copyright (c) 2002-2006 Novell, Inc.
  */
 
 #include <config.h>
@@ -97,15 +98,32 @@ static guint32 unicode_bytes (const gunichar2 *str)
        } while(1);
 }
 
+static gunichar2*
+unicode_get (const gunichar2 *str)
+{
+       gunichar2 *swapped;
+       int i, len;
+
+       len = unicode_bytes (str);
+       swapped = g_malloc0 (len);
+       i = 0;
+       while (str [i]) {
+               swapped [i] = GUINT16_FROM_LE (str [i]);
+               i ++;
+       }
+
+       return swapped;
+}
+
 /* 
- * compare a null-terminated utf16 string and a normal string.
+ * compare a little-endian 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 guchar *str2)
+unicode_string_equals (const gunichar2 *str1, const gchar *str2)
 {
        while (*str1 && *str2) {
-               if (*str1 != *str2)
+               if (GUINT16_TO_LE (*str1) != *str2)
                        return FALSE;
                ++str1;
                ++str2;
@@ -113,7 +131,7 @@ unicode_string_equals (const gunichar2 *str1, const guchar *str2)
        return *str1 == *str2;
 }
 
-static void process_set_field_object (MonoObject *obj, const guchar *fieldname,
+static void process_set_field_object (MonoObject *obj, const gchar *fieldname,
                                      MonoObject *data)
 {
        MonoClassField *field;
@@ -125,10 +143,11 @@ static void process_set_field_object (MonoObject *obj, const guchar *fieldname,
 
        field=mono_class_get_field_from_name (mono_object_class (obj),
                                              fieldname);
+       /* FIXME: moving GC */
        *(MonoObject **)(((char *)obj) + field->offset)=data;
 }
 
-static void process_set_field_string (MonoObject *obj, const guchar *fieldname,
+static void process_set_field_string (MonoObject *obj, const gchar *fieldname,
                                      const gunichar2 *val, guint32 len)
 {
        MonoClassField *field;
@@ -143,12 +162,13 @@ static void process_set_field_string (MonoObject *obj, const guchar *fieldname,
        
        field=mono_class_get_field_from_name (mono_object_class (obj),
                                              fieldname);
+       /* FIXME: moving GC */
        *(MonoString **)(((char *)obj) + field->offset)=string;
 }
 
 static void process_set_field_string_utf8 (MonoObject *obj,
-                                          const guchar *fieldname,
-                                          const guchar *val)
+                                          const gchar *fieldname,
+                                          const gchar *val)
 {
        MonoClassField *field;
        MonoString *string;
@@ -162,10 +182,11 @@ static void process_set_field_string_utf8 (MonoObject *obj,
        
        field=mono_class_get_field_from_name (mono_object_class (obj),
                                              fieldname);
+       /* FIXME: moving GC */
        *(MonoString **)(((char *)obj) + field->offset)=string;
 }
 
-static void process_set_field_int (MonoObject *obj, const guchar *fieldname,
+static void process_set_field_int (MonoObject *obj, const gchar *fieldname,
                                   guint32 val)
 {
        MonoClassField *field;
@@ -180,7 +201,7 @@ static void process_set_field_int (MonoObject *obj, const guchar *fieldname,
        *(guint32 *)(((char *)obj) + field->offset)=val;
 }
 
-static void process_set_field_bool (MonoObject *obj, const guchar *fieldname,
+static void process_set_field_bool (MonoObject *obj, const gchar *fieldname,
                                    gboolean val)
 {
        MonoClassField *field;
@@ -208,13 +229,13 @@ typedef struct {
 static gpointer process_get_versioninfo_block (gpointer data,
                                               version_data *block)
 {
-       block->data_len=*((guint16 *)data);
+       block->data_len=GUINT16_TO_LE (*((guint16 *)data));
        data = (char *)data + sizeof(guint16);
-       block->value_len=*((guint16 *)data);
+       block->value_len=GUINT16_TO_LE (*((guint16 *)data));
        data = (char *)data + sizeof(guint16);
 
        /* No idea what the type is supposed to indicate */
-       block->type=*((guint16 *)data);
+       block->type=GUINT16_TO_LE (*((guint16 *)data));
        data = (char *)data + sizeof(guint16);
        block->key=((gunichar2 *)data);
 
@@ -257,7 +278,7 @@ static gpointer process_read_string_block (MonoObject *filever,
                                           gboolean store)
 {
        version_data block;
-       guint16 string_len=0;
+       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)
@@ -284,7 +305,7 @@ static gpointer process_read_string_block (MonoObject *filever,
                }
                
                string_len=string_len+block.data_len;
-               value=(gunichar2 *)data_ptr;
+               value=unicode_get ((gunichar2 *)data_ptr);
                /* Skip over the value */
                data_ptr=((gunichar2 *)data_ptr)+block.value_len;
 
@@ -319,6 +340,7 @@ static gpointer process_read_string_block (MonoObject *filever,
                                 */
                        }
                }
+               g_free (value);
        }
        
        return(data_ptr);
@@ -334,6 +356,7 @@ static gpointer process_read_stringtable_block (MonoObject *filever,
                                                guint16 data_len)
 {
        version_data block;
+       gunichar2 *value;
        gchar *language;
        guint16 string_len=36;  /* length of the StringFileInfo block */
 
@@ -361,9 +384,14 @@ static gpointer process_read_stringtable_block (MonoObject *filever,
                }
                string_len=string_len+block.data_len;
 
-               language = g_utf16_to_utf8 (block.key, unicode_bytes (block.key), NULL, NULL, NULL);
+               value = unicode_get (block.key);
+               language = g_utf16_to_utf8 (value, unicode_bytes (block.key), NULL, NULL, NULL);
                g_strdown (language);
-               if (!strcmp (language, "007f04b0") || !strcmp (language, "000004b0")) {
+
+               /* Kludge: treat en_US as neutral too */
+               if (!strcmp (language, "007f04b0") ||
+                   !strcmp (language, "000004b0") ||
+                   !strcmp (language, "040904b0")) {
                        /* Got the one we're interested in */
                        process_set_field_string_utf8 (filever, "language",
                                                       "Language Neutral");
@@ -380,7 +408,8 @@ static gpointer process_read_stringtable_block (MonoObject *filever,
                                                            FALSE);
                }
                g_free (language);
-
+               g_free (value);
+               
                if(data_ptr==NULL) {
                        /* Child block hit padding */
 #ifdef DEBUG
@@ -453,6 +482,7 @@ static void process_get_fileversion (MonoObject *filever, MonoImage *image)
        
        data=mono_image_rva_map (image,
                               version_info->rde_data_offset);
+       g_free (version_info);
        if(data==NULL) {
                return;
        }
@@ -583,6 +613,7 @@ static void process_add_module (GPtrArray *modules, MonoAssembly *ass)
        process_set_field_string_utf8 (item, "modulename", modulename);
        g_free (modulename);
 
+       /* FIXME: moving GC */
        g_ptr_array_add (modules, item);
 }
 
@@ -626,11 +657,10 @@ MonoArray *ves_icall_System_Diagnostics_Process_GetModules_internal (MonoObject
                            modules_list->len);
        
        for(i=0; i<modules_list->len; i++) {
-               mono_array_set (arr, MonoObject *, i,
-                               g_ptr_array_index (modules_list, i));
+               mono_array_setref (arr, i, g_ptr_array_index (modules_list, i));
        }
        
-       g_ptr_array_free (modules_list, FALSE);
+       g_ptr_array_free (modules_list, TRUE);
        
        return(arr);
 }
@@ -638,14 +668,14 @@ MonoArray *ves_icall_System_Diagnostics_Process_GetModules_internal (MonoObject
 void ves_icall_System_Diagnostics_FileVersionInfo_GetVersionInfo_internal (MonoObject *this, MonoString *filename)
 {
        MonoImage *image;
-       guchar *filename_utf8;
+       gchar *filename_utf8;
        
        MONO_ARCH_SAVE_REGS;
 
        STASH_SYS_ASS (this);
        
-       filename_utf8=mono_string_to_utf8 (filename);
-       image=mono_image_open (filename_utf8, NULL);
+       filename_utf8 = mono_string_to_utf8 (filename);
+       image = mono_pe_file_open (filename_utf8, NULL);
        g_free (filename_utf8);
        
        if(image==NULL) {
@@ -714,7 +744,54 @@ complete_path (const gunichar2 *appname, gchar **completed)
        return TRUE;
 }
 
-MonoBoolean ves_icall_System_Diagnostics_Process_Start_internal (MonoString *appname, MonoString *cmd, MonoString *dirname, HANDLE stdin_handle, HANDLE stdout_handle, HANDLE stderr_handle, MonoProcInfo *process_info)
+MonoBoolean ves_icall_System_Diagnostics_Process_ShellExecuteEx_internal (MonoProcessStartInfo *proc_start_info, MonoProcInfo *process_info)
+{
+       SHELLEXECUTEINFO shellex = {0};
+       gboolean ret;
+
+       shellex.cbSize = sizeof(SHELLEXECUTEINFO);
+       shellex.fMask = SEE_MASK_FLAG_DDEWAIT | SEE_MASK_NOCLOSEPROCESS | SEE_MASK_UNICODE;
+       shellex.nShow = SW_SHOWNORMAL;
+       
+       if (proc_start_info->filename != NULL) {
+               shellex.lpFile = mono_string_chars (proc_start_info->filename);
+       }
+
+       if (proc_start_info->arguments != NULL) {
+               shellex.lpParameters = mono_string_chars (proc_start_info->arguments);
+       }
+
+       if (proc_start_info->verb != NULL &&
+           mono_string_length (proc_start_info->verb) != 0) {
+               shellex.lpVerb = mono_string_chars (proc_start_info->verb);
+       }
+
+       if (proc_start_info->working_directory != NULL &&
+           mono_string_length (proc_start_info->working_directory) != 0) {
+               shellex.lpDirectory = mono_string_chars (proc_start_info->working_directory);
+       }
+
+       ret = ShellExecuteEx (&shellex);
+       if (ret == FALSE) {
+               process_info->pid = -GetLastError ();
+       } else {
+               process_info->process_handle = shellex.hProcess;
+               process_info->thread_handle = NULL;
+               /* It appears that there's no way to get the pid from a
+                * process handle before windows xp.  Really.
+                */
+#ifdef HAVE_GETPROCESSID
+               process_info->pid = GetProcessId (shellex.hProcess);
+#else
+               process_info->pid = 0;
+#endif
+               process_info->tid = 0;
+       }
+
+       return (ret);
+}
+
+MonoBoolean ves_icall_System_Diagnostics_Process_CreateProcess_internal (MonoProcessStartInfo *proc_start_info, HANDLE stdin_handle, HANDLE stdout_handle, HANDLE stderr_handle, MonoProcInfo *process_info)
 {
        gboolean ret;
        gunichar2 *dir;
@@ -723,72 +800,46 @@ MonoBoolean ves_icall_System_Diagnostics_Process_Start_internal (MonoString *app
        gunichar2 *shell_path = NULL;
        gchar *env_vars = NULL;
        gboolean free_shell_path = TRUE;
-       gchar *newcmd, *tmp;
+       gchar *spath = NULL;
+       MonoString *cmd = proc_start_info->arguments;
+       guint32 creation_flags, logon_flags;
        
-       MONO_ARCH_SAVE_REGS;
-
        startinfo.cb=sizeof(STARTUPINFO);
        startinfo.dwFlags=STARTF_USESTDHANDLES;
        startinfo.hStdInput=stdin_handle;
        startinfo.hStdOutput=stdout_handle;
        startinfo.hStdError=stderr_handle;
-       
-       if (process_info->use_shell) {
-               const gchar *spath;
-               const gchar *shell_args;
-#ifdef PLATFORM_WIN32
-               spath = g_getenv ("COMSPEC");
-               shell_args = "/c %s";
-#else
-               spath = g_getenv ("SHELL");
-               shell_args = "-c %s";
-#endif
-               if (spath != NULL) {
-                       gint dummy;
-                       gchar *quoted;
 
-                       shell_path = mono_unicode_from_external (spath, &dummy);
-                       tmp = mono_string_to_utf8 (cmd);
-                       quoted = g_shell_quote (tmp);
-#ifdef PLATFORM_WIN32
-                       {
-                               gchar *q = quoted;
-                               while (*q) {
-                                       if (*q == '\'')
-                                               *q = '\"';
-                                       q++;
-                               }
-                       }
-#endif
-                       newcmd = g_strdup_printf (shell_args, quoted);
-                       g_free (quoted);
-                       g_free (tmp);
-                       cmd = mono_string_new (mono_domain_get (), newcmd);
-                       g_free (newcmd);
-               }
-       } else {
-               gchar *spath = NULL;
-               shell_path = mono_string_chars (appname);
-               complete_path (shell_path, &spath);
-               if (spath == NULL) {
-                       process_info->pid = -ERROR_FILE_NOT_FOUND;
-                       return FALSE;
-               }
+       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);
+       if (spath == NULL) {
+               process_info->pid = -ERROR_FILE_NOT_FOUND;
+               return FALSE;
+       }
 #ifdef PLATFORM_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;
-               free_shell_path = FALSE;
+       /* Seems like our CreateProcess does not work as the windows one.
+        * This hack is needed to deal with paths containing spaces */
+       shell_path = NULL;
+       free_shell_path = FALSE;
+       if (cmd) {
+               gchar *newcmd, *tmp;
                tmp = mono_string_to_utf8 (cmd);
                newcmd = g_strdup_printf ("%s %s", spath, tmp);
                cmd = mono_string_new_wrapper (newcmd);
-               g_free (newcmd);
                g_free (tmp);
+               g_free (newcmd);
+       }
+       else {
+               cmd = mono_string_new_wrapper (spath);
+       }
 #else
-               shell_path = g_utf8_to_utf16 (spath, -1, NULL, NULL, NULL);
+       shell_path = g_utf8_to_utf16 (spath, -1, NULL, NULL, NULL);
 #endif
-               g_free (spath);
-       }
+       g_free (spath);
 
        if (process_info->env_keys != NULL) {
                gint i, len; 
@@ -834,13 +885,18 @@ MonoBoolean ves_icall_System_Diagnostics_Process_Start_internal (MonoString *app
        /* The default dir name is "".  Turn that into NULL to mean
         * "current directory"
         */
-       if(mono_string_length (dirname)==0) {
+       if(mono_string_length (proc_start_info->working_directory)==0) {
                dir=NULL;
        } else {
-               dir=mono_string_chars (dirname);
+               dir=mono_string_chars (proc_start_info->working_directory);
+       }
+
+       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);
        }
-       
-       ret=CreateProcess (shell_path, mono_string_chars (cmd), NULL, NULL, TRUE, CREATE_UNICODE_ENVIRONMENT, env_vars, dir, &startinfo, &procinfo);
 
        g_free (env_vars);
        if (free_shell_path)
@@ -850,7 +906,8 @@ MonoBoolean ves_icall_System_Diagnostics_Process_Start_internal (MonoString *app
                process_info->process_handle=procinfo.hProcess;
                /*process_info->thread_handle=procinfo.hThread;*/
                process_info->thread_handle=NULL;
-               CloseHandle(procinfo.hThread);
+               if (procinfo.hThread != NULL && procinfo.hThread != INVALID_HANDLE_VALUE)
+                       CloseHandle(procinfo.hThread);
                process_info->pid=procinfo.dwProcessId;
                process_info->tid=procinfo.dwThreadId;
        } else {
@@ -950,7 +1007,7 @@ MonoString *ves_icall_System_Diagnostics_Process_ProcessName_internal (HANDLE pr
                return(NULL);
        }
        
-       len=GetModuleBaseName (process, mod, name, sizeof(name));
+       len=GetModuleBaseName (process, mod, name, MAX_PATH);
        if(len==0) {
                return(NULL);
        }
@@ -1039,3 +1096,34 @@ ves_icall_System_Diagnostics_Process_Kill_internal (HANDLE process, gint32 sig)
        return TerminateProcess (process, -sig);
 }
 
+gint64
+ves_icall_System_Diagnostics_Process_Times (HANDLE process, gint32 type)
+{
+       FILETIME create_time, exit_time, kernel_time, user_time;
+       
+       if (GetProcessTimes (process, &create_time, &exit_time, &kernel_time, &user_time)) {
+               if (type == 0)
+                       return *(gint64*)&user_time;
+               else if (type == 1)
+                       return *(gint64*)&kernel_time;
+               /* system + user time: FILETIME can be (memory) cast to a 64 bit int */
+               return *(gint64*)&kernel_time + *(gint64*)&user_time;
+       }
+       return 0;
+}
+
+gint32
+ves_icall_System_Diagnostics_Process_GetPriorityClass (HANDLE process, gint32 *error)
+{
+       gint32 ret = GetPriorityClass (process);
+       *error = ret == 0 ? GetLastError () : 0;
+       return ret;
+}
+
+MonoBoolean
+ves_icall_System_Diagnostics_Process_SetPriorityClass (HANDLE process, gint32 priority_class, gint32 *error)
+{
+       gboolean ret = SetPriorityClass (process, priority_class);
+       *error = ret == 0 ? GetLastError () : 0;
+       return ret;
+}