1 //------------------------------------------------------------------------------
3 // System.Environment.cs
5 // Copyright (C) 2001 Moonlight Enterprises, All Rights Reserved
7 // Author: Jim Richardson, develop@wtfo-guru.com
8 // Dan Lewis (dihlewis@yahoo.co.uk)
9 // Created: Saturday, August 11, 2001
11 //------------------------------------------------------------------------------
13 // Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
15 // Permission is hereby granted, free of charge, to any person obtaining
16 // a copy of this software and associated documentation files (the
17 // "Software"), to deal in the Software without restriction, including
18 // without limitation the rights to use, copy, modify, merge, publish,
19 // distribute, sublicense, and/or sell copies of the Software, and to
20 // permit persons to whom the Software is furnished to do so, subject to
21 // the following conditions:
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36 using System.Collections;
37 using System.Runtime.CompilerServices;
38 using System.Security;
39 using System.Security.Permissions;
41 using System.Runtime.InteropServices;
46 public static class Environment {
49 * This is the version number of the corlib-runtime interface. When
50 * making changes to this interface (by changing the layout
51 * of classes the runtime knows about, changing icall signature or
52 * semantics etc), increment this variable. Also increment the
53 * pair of this variable in the runtime in metadata/appdomain.c.
54 * Changes which are already detected at runtime, like the addition
55 * of icalls, do not require an increment.
57 #pragma warning disable 169
58 private const int mono_corlib_version = 94;
59 #pragma warning restore 169
62 public enum SpecialFolder
75 DesktopDirectory = 0x10,
77 ApplicationData = 0x1a,
78 LocalApplicationData = 0x1c,
82 CommonApplicationData = 0x23,
86 CommonProgramFiles = 0x2b,
87 #if NET_4_0 || MOONLIGHT
91 NetworkShortcuts = 0x13,
93 CommonStartMenu = 0x16,
94 CommonPrograms = 0x17,
96 CommonDesktopDirectory = 0x19,
97 PrinterShortcuts = 0x1b,
101 ProgramFilesX86 = 0x2a,
102 CommonProgramFilesX86 = 0x2c,
103 CommonTemplates = 0x2d,
104 CommonDocuments = 0x2e,
105 CommonAdminTools = 0x2f,
108 CommonPictures = 0x36,
111 LocalizedResources = 0x39,
112 CommonOemLinks = 0x3a,
120 enum SpecialFolderOption {
122 DoNotVerify = 0x4000,
127 /// Gets the command line for this process
129 public static string CommandLine {
130 // note: security demand inherited from calling GetCommandLineArgs
132 StringBuilder sb = new StringBuilder ();
133 foreach (string str in GetCommandLineArgs ()) {
137 for (int i = 0; i < s.Length; i++) {
138 if (quote.Length == 0 && Char.IsWhiteSpace (s [i])) {
140 } else if (s [i] == '"') {
144 if (escape && quote.Length != 0) {
145 s = s.Replace ("\"", "\\\"");
147 sb.AppendFormat ("{0}{1}{0} ", quote, s);
151 return sb.ToString ();
156 /// Gets or sets the current directory. Actually this is supposed to get
157 /// and/or set the process start directory acording to the documentation
158 /// but actually test revealed at beta2 it is just Getting/Setting the CurrentDirectory
160 public static string CurrentDirectory
163 return Directory.GetCurrentDirectory ();
166 Directory.SetCurrentDirectory (value);
171 /// Gets or sets the exit code of this process
173 public extern static int ExitCode
175 [MethodImplAttribute (MethodImplOptions.InternalCall)]
177 [MethodImplAttribute (MethodImplOptions.InternalCall)]
181 static public extern bool HasShutdownStarted
183 [MethodImplAttribute (MethodImplOptions.InternalCall)]
189 /// Gets the name of the local computer
191 public extern static string MachineName {
192 [MethodImplAttribute (MethodImplOptions.InternalCall)]
193 [EnvironmentPermission (SecurityAction.Demand, Read="COMPUTERNAME")]
194 [SecurityPermission (SecurityAction.Demand, UnmanagedCode=true)]
199 /// Gets the standard new line value
201 public extern static string NewLine {
202 [MethodImplAttribute (MethodImplOptions.InternalCall)]
207 // Support methods and fields for OSVersion property
209 static OperatingSystem os;
211 static extern PlatformID Platform {
212 [MethodImplAttribute (MethodImplOptions.InternalCall)]
216 [MethodImplAttribute (MethodImplOptions.InternalCall)]
217 internal static extern string GetOSVersionString ();
220 /// Gets the current OS version information
222 public static OperatingSystem OSVersion {
225 Version v = Version.CreateFromString (GetOSVersionString ());
226 PlatformID p = Platform;
227 if (p == PlatformID.MacOSX)
229 os = new OperatingSystem (p, v);
238 public static string StackTrace {
239 [EnvironmentPermission (SecurityAction.Demand, Unrestricted=true)]
241 System.Diagnostics.StackTrace trace = new System.Diagnostics.StackTrace (0, true);
242 return trace.ToString ();
247 /// Get a fully qualified path to the system directory
249 public static string SystemDirectory {
251 return GetFolderPath (SpecialFolder.System);
256 /// Get the number of milliseconds that have elapsed since the system was booted
258 public extern static int TickCount {
259 [MethodImplAttribute (MethodImplOptions.InternalCall)]
264 /// Get UserDomainName
266 public static string UserDomainName {
267 // FIXME: this variable doesn't exist (at least not on WinXP) - reported to MS as FDBK20562
268 [EnvironmentPermission (SecurityAction.Demand, Read="USERDOMAINNAME")]
275 /// Gets a flag indicating whether the process is in interactive mode
277 [MonoTODO ("Currently always returns false, regardless of interactive state")]
278 public static bool UserInteractive {
285 /// Get the user name of current process is running under
287 public extern static string UserName {
288 [MethodImplAttribute (MethodImplOptions.InternalCall)]
289 [EnvironmentPermission (SecurityAction.Demand, Read="USERNAME;USER")]
294 /// Get the version of the common language runtime
296 public static Version Version {
298 return new Version (Consts.FxFileVersion);
303 /// Get the amount of physical memory mapped to process
305 [MonoTODO ("Currently always returns zero")]
306 public static long WorkingSet {
307 [EnvironmentPermission (SecurityAction.Demand, Unrestricted=true)]
311 [MethodImplAttribute (MethodImplOptions.InternalCall)]
312 [SecurityPermission (SecurityAction.Demand, UnmanagedCode=true)]
313 public extern static void Exit (int exitCode);
316 /// Substitute environment variables in the argument "name"
318 public static string ExpandEnvironmentVariables (string name)
321 throw new ArgumentNullException ("name");
323 int off1 = name.IndexOf ('%');
327 int len = name.Length;
329 if (off1 == len - 1 || (off2 = name.IndexOf ('%', off1 + 1)) == -1)
332 StringBuilder result = new StringBuilder ();
333 result.Append (name, 0, off1);
334 Hashtable tbl = null;
336 string var = name.Substring (off1 + 1, off2 - off1 - 1);
337 string value = GetEnvironmentVariable (var);
338 if (value == null && Environment.IsRunningOnWindows) {
339 // On windows, env. vars. are case insensitive
341 tbl = GetEnvironmentVariablesNoCase ();
343 value = tbl [var] as string;
346 // If value not found, add %FOO to stream,
347 // and use the closing % for the next iteration.
348 // If value found, expand it in place of %FOO%
354 result.Append (value);
357 off1 = name.IndexOf ('%', off2 + 1);
358 // If no % found for off1, don't look for one for off2
359 off2 = (off1 == -1 || off2 > len-1)? -1 :name.IndexOf ('%', off1 + 1);
360 // textLen is the length of text between the closing % of current iteration
361 // and the starting % of the next iteration if any. This text is added to output
363 // If no new % found, use all the remaining text
364 if (off1 == -1 || off2 == -1)
365 textLen = len - oldOff2 - 1;
366 // If value found in current iteration, use text after current closing % and next %
367 else if(value != null)
368 textLen = off1 - oldOff2 - 1;
369 // If value not found in current iteration, but a % was found for next iteration,
370 // use text from current closing % to the next %.
372 textLen = off1 - oldOff2;
373 if(off1 >= oldOff2 || off1 == -1)
374 result.Append (name, oldOff2+1, textLen);
375 } while (off2 > -1 && off2 < len);
377 return result.ToString ();
382 /// Return an array of the command line arguments of the current process
384 [MethodImplAttribute (MethodImplOptions.InternalCall)]
385 [EnvironmentPermissionAttribute (SecurityAction.Demand, Read = "PATH")]
386 public extern static string[] GetCommandLineArgs ();
388 [MethodImplAttribute (MethodImplOptions.InternalCall)]
389 internal extern static string internalGetEnvironmentVariable (string variable);
392 /// Return a string containing the value of the environment
393 /// variable identifed by parameter "variable"
395 public static string GetEnvironmentVariable (string variable)
398 if (SecurityManager.SecurityEnabled) {
399 new EnvironmentPermission (EnvironmentPermissionAccess.Read, variable).Demand ();
402 return internalGetEnvironmentVariable (variable);
405 static Hashtable GetEnvironmentVariablesNoCase ()
407 Hashtable vars = new Hashtable (CaseInsensitiveHashCodeProvider.Default,
408 CaseInsensitiveComparer.Default);
410 foreach (string name in GetEnvironmentVariableNames ()) {
411 vars [name] = internalGetEnvironmentVariable (name);
418 /// Return a set of all environment variables and their values
421 public static IDictionary GetEnvironmentVariables ()
423 StringBuilder sb = null;
424 if (SecurityManager.SecurityEnabled) {
425 // we must have access to each variable to get the lot
426 sb = new StringBuilder ();
427 // but (performance-wise) we do not want a stack-walk
428 // for each of them so we concatenate them
431 Hashtable vars = new Hashtable ();
432 foreach (string name in GetEnvironmentVariableNames ()) {
433 vars [name] = internalGetEnvironmentVariable (name);
441 new EnvironmentPermission (EnvironmentPermissionAccess.Read, sb.ToString ()).Demand ();
446 [EnvironmentPermission (SecurityAction.Demand, Unrestricted=true)]
447 public static IDictionary GetEnvironmentVariables ()
449 Hashtable vars = new Hashtable ();
450 foreach (string name in GetEnvironmentVariableNames ()) {
451 vars [name] = internalGetEnvironmentVariable (name);
457 [MethodImplAttribute (MethodImplOptions.InternalCall)]
458 private extern static string GetWindowsFolderPath (int folder);
461 /// Returns the fully qualified path of the
462 /// folder specified by the "folder" parameter
464 public static string GetFolderPath (SpecialFolder folder)
466 return GetFolderPath (folder, SpecialFolderOption.None);
469 [MonoTODO ("Figure out the folder path for all the new values in SpecialFolder. Use the 'option' argument.")]
472 static string GetFolderPath(SpecialFolder folder, SpecialFolderOption option)
474 SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
478 if (Environment.IsRunningOnWindows) {
479 dir = GetWindowsFolderPath ((int) folder);
481 dir = InternalGetFolderPath (folder);
484 if ((dir != null) && (dir.Length > 0) && SecurityManager.SecurityEnabled) {
485 new FileIOPermission (FileIOPermissionAccess.PathDiscovery, dir).Demand ();
491 private static string ReadXdgUserDir (string config_dir, string home_dir,
492 string key, string fallback)
494 string env_path = internalGetEnvironmentVariable (key);
495 if (env_path != null && env_path != String.Empty) {
499 string user_dirs_path = Path.Combine (config_dir, "user-dirs.dirs");
501 if (!File.Exists (user_dirs_path)) {
502 return Path.Combine (home_dir, fallback);
506 using(StreamReader reader = new StreamReader (user_dirs_path)) {
508 while ((line = reader.ReadLine ()) != null) {
510 int delim_index = line.IndexOf ('=');
511 if(delim_index > 8 && line.Substring (0, delim_index) == key) {
512 string path = line.Substring (delim_index + 1).Trim ('"');
513 bool relative = false;
515 if (path.StartsWith ("$HOME/")) {
517 path = path.Substring (6);
518 } else if (!path.StartsWith ("/")) {
522 return relative ? Path.Combine (home_dir, path) : path;
526 } catch (FileNotFoundException) {
529 return Path.Combine (home_dir, fallback);
533 // the security runtime (and maybe other parts of corlib) needs the
534 // information to initialize themselves before permissions can be checked
535 internal static string InternalGetFolderPath (SpecialFolder folder)
537 string home = internalGetHome ();
539 // http://freedesktop.org/Standards/basedir-spec/basedir-spec-0.6.html
541 // note: skip security check for environment variables
542 string data = internalGetEnvironmentVariable ("XDG_DATA_HOME");
543 if ((data == null) || (data == String.Empty)) {
544 data = Path.Combine (home, ".local");
545 data = Path.Combine (data, "share");
548 // note: skip security check for environment variables
549 string config = internalGetEnvironmentVariable ("XDG_CONFIG_HOME");
550 if ((config == null) || (config == String.Empty)) {
551 config = Path.Combine (home, ".config");
555 // MyComputer is a virtual directory
556 case SpecialFolder.MyComputer:
560 case SpecialFolder.Personal:
562 return Path.Combine (home, "Documents");
566 // use FDO's CONFIG_HOME. This data will be synced across a network like the windows counterpart.
567 case SpecialFolder.ApplicationData:
570 string dir = Path.Combine (Path.Combine (home, "Documents"), ".config");
571 if (!Directory.Exists (dir))
572 Directory.CreateDirectory (dir);
579 //use FDO's DATA_HOME. This is *NOT* synced
580 case SpecialFolder.LocalApplicationData:
583 string dir = Path.Combine (home, "Documents");
584 if (!Directory.Exists (dir))
585 Directory.CreateDirectory (dir);
593 case SpecialFolder.Desktop:
594 case SpecialFolder.DesktopDirectory:
595 return ReadXdgUserDir (config, home, "XDG_DESKTOP_DIR", "Desktop");
597 case SpecialFolder.MyMusic:
598 return ReadXdgUserDir (config, home, "XDG_MUSIC_DIR", "Music");
600 case SpecialFolder.MyPictures:
601 return ReadXdgUserDir (config, home, "XDG_PICTURES_DIR", "Pictures");
603 case SpecialFolder.Templates:
604 return ReadXdgUserDir (config, home, "XDG_TEMPLATES_DIR", "Templates");
605 #if NET_4_0 || MOONLIGHT
606 case SpecialFolder.MyVideos:
607 return ReadXdgUserDir (config, home, "XDG_VIDEOS_DIR", "Videos");
610 case SpecialFolder.CommonTemplates:
611 return "/usr/share/templates";
612 case SpecialFolder.Fonts:
613 return Path.Combine (home, ".fonts");
615 // these simply dont exist on Linux
616 // The spec says if a folder doesnt exist, we
618 case SpecialFolder.Favorites:
619 case SpecialFolder.Programs:
620 case SpecialFolder.SendTo:
621 case SpecialFolder.StartMenu:
622 case SpecialFolder.Startup:
623 case SpecialFolder.Cookies:
624 case SpecialFolder.History:
625 case SpecialFolder.InternetCache:
626 case SpecialFolder.Recent:
627 case SpecialFolder.CommonProgramFiles:
628 case SpecialFolder.ProgramFiles:
629 case SpecialFolder.System:
631 case SpecialFolder.NetworkShortcuts:
632 case SpecialFolder.CommonStartMenu:
633 case SpecialFolder.CommonPrograms:
634 case SpecialFolder.CommonStartup:
635 case SpecialFolder.CommonDesktopDirectory:
636 case SpecialFolder.PrinterShortcuts:
637 case SpecialFolder.Windows:
638 case SpecialFolder.UserProfile:
639 case SpecialFolder.SystemX86:
640 case SpecialFolder.ProgramFilesX86:
641 case SpecialFolder.CommonProgramFilesX86:
642 case SpecialFolder.CommonDocuments:
643 case SpecialFolder.CommonAdminTools:
644 case SpecialFolder.AdminTools:
645 case SpecialFolder.CommonMusic:
646 case SpecialFolder.CommonPictures:
647 case SpecialFolder.CommonVideos:
648 case SpecialFolder.Resources:
649 case SpecialFolder.LocalizedResources:
650 case SpecialFolder.CommonOemLinks:
651 case SpecialFolder.CDBurning:
654 // This is where data common to all users goes
655 case SpecialFolder.CommonApplicationData:
658 throw new ArgumentException ("Invalid SpecialFolder");
662 [EnvironmentPermission (SecurityAction.Demand, Unrestricted=true)]
663 public static string[] GetLogicalDrives ()
665 return GetLogicalDrivesInternal ();
669 [MethodImplAttribute (MethodImplOptions.InternalCall)]
670 private static extern void internalBroadcastSettingChange ();
672 public static string GetEnvironmentVariable (string variable, EnvironmentVariableTarget target)
675 case EnvironmentVariableTarget.Process:
676 return GetEnvironmentVariable (variable);
677 case EnvironmentVariableTarget.Machine:
678 new EnvironmentPermission (PermissionState.Unrestricted).Demand ();
679 if (!IsRunningOnWindows)
681 using (Microsoft.Win32.RegistryKey env = Microsoft.Win32.Registry.LocalMachine.OpenSubKey (@"SYSTEM\CurrentControlSet\Control\Session Manager\Environment")) {
682 object regvalue = env.GetValue (variable);
683 return (regvalue == null) ? null : regvalue.ToString ();
685 case EnvironmentVariableTarget.User:
686 new EnvironmentPermission (PermissionState.Unrestricted).Demand ();
687 if (!IsRunningOnWindows)
689 using (Microsoft.Win32.RegistryKey env = Microsoft.Win32.Registry.CurrentUser.OpenSubKey ("Environment", false)) {
690 object regvalue = env.GetValue (variable);
691 return (regvalue == null) ? null : regvalue.ToString ();
694 throw new ArgumentException ("target");
698 public static IDictionary GetEnvironmentVariables (EnvironmentVariableTarget target)
700 IDictionary variables = (IDictionary)new Hashtable ();
702 case EnvironmentVariableTarget.Process:
703 variables = GetEnvironmentVariables ();
705 case EnvironmentVariableTarget.Machine:
706 new EnvironmentPermission (PermissionState.Unrestricted).Demand ();
707 if (IsRunningOnWindows) {
708 using (Microsoft.Win32.RegistryKey env = Microsoft.Win32.Registry.LocalMachine.OpenSubKey (@"SYSTEM\CurrentControlSet\Control\Session Manager\Environment")) {
709 string[] value_names = env.GetValueNames ();
710 foreach (string value_name in value_names)
711 variables.Add (value_name, env.GetValue (value_name));
715 case EnvironmentVariableTarget.User:
716 new EnvironmentPermission (PermissionState.Unrestricted).Demand ();
717 if (IsRunningOnWindows) {
718 using (Microsoft.Win32.RegistryKey env = Microsoft.Win32.Registry.CurrentUser.OpenSubKey ("Environment")) {
719 string[] value_names = env.GetValueNames ();
720 foreach (string value_name in value_names)
721 variables.Add (value_name, env.GetValue (value_name));
726 throw new ArgumentException ("target");
731 [EnvironmentPermission (SecurityAction.Demand, Unrestricted=true)]
732 public static void SetEnvironmentVariable (string variable, string value)
734 SetEnvironmentVariable (variable, value, EnvironmentVariableTarget.Process);
737 [EnvironmentPermission (SecurityAction.Demand, Unrestricted = true)]
738 public static void SetEnvironmentVariable (string variable, string value, EnvironmentVariableTarget target)
740 if (variable == null)
741 throw new ArgumentNullException ("variable");
742 if (variable == String.Empty)
743 throw new ArgumentException ("String cannot be of zero length.", "variable");
744 if (variable.IndexOf ('=') != -1)
745 throw new ArgumentException ("Environment variable name cannot contain an equal character.", "variable");
746 if (variable[0] == '\0')
747 throw new ArgumentException ("The first char in the string is the null character.", "variable");
750 case EnvironmentVariableTarget.Process:
751 InternalSetEnvironmentVariable (variable, value);
753 case EnvironmentVariableTarget.Machine:
754 if (!IsRunningOnWindows)
756 using (Microsoft.Win32.RegistryKey env = Microsoft.Win32.Registry.LocalMachine.OpenSubKey (@"SYSTEM\CurrentControlSet\Control\Session Manager\Environment", true)) {
757 if (String.IsNullOrEmpty (value))
758 env.DeleteValue (variable, false);
760 env.SetValue (variable, value);
761 internalBroadcastSettingChange ();
764 case EnvironmentVariableTarget.User:
765 if (!IsRunningOnWindows)
767 using (Microsoft.Win32.RegistryKey env = Microsoft.Win32.Registry.CurrentUser.OpenSubKey ("Environment", true)) {
768 if (String.IsNullOrEmpty (value))
769 env.DeleteValue (variable, false);
771 env.SetValue (variable, value);
772 internalBroadcastSettingChange ();
776 throw new ArgumentException ("target");
780 [MethodImplAttribute (MethodImplOptions.InternalCall)]
781 internal static extern void InternalSetEnvironmentVariable (string variable, string value);
783 [SecurityPermission (SecurityAction.LinkDemand, UnmanagedCode=true)]
784 public static void FailFast (string message)
786 throw new NotImplementedException ();
789 #if NET_4_0 || MOONLIGHT
791 public static void FailFast (string message, Exception exception)
793 throw new NotImplementedException ();
798 public static bool Is64BitOperatingSystem {
799 get { return IntPtr.Size == 8; } // FIXME: is this good enough?
802 public static bool Is64BitProcess {
803 get { return Is64BitOperatingSystem; }
806 public static int SystemPageSize {
807 get { return GetPageSize (); }
811 public static extern int ProcessorCount {
812 [EnvironmentPermission (SecurityAction.Demand, Read="NUMBER_OF_PROCESSORS")]
813 [MethodImplAttribute (MethodImplOptions.InternalCall)]
819 internal static bool IsRunningOnWindows {
820 get { return ((int) Platform < 4); }
824 // Used by gacutil.exe
826 #pragma warning disable 169
827 private static string GacPath {
829 if (Environment.IsRunningOnWindows) {
830 /* On windows, we don't know the path where mscorlib.dll will be installed */
831 string corlibDir = new DirectoryInfo (Path.GetDirectoryName (typeof (int).Assembly.Location)).Parent.Parent.FullName;
832 return Path.Combine (Path.Combine (corlibDir, "mono"), "gac");
835 return Path.Combine (Path.Combine (internalGetGacPath (), "mono"), "gac");
838 #pragma warning restore 169
839 [MethodImplAttribute (MethodImplOptions.InternalCall)]
840 internal extern static string internalGetGacPath ();
842 [MethodImplAttribute (MethodImplOptions.InternalCall)]
843 private extern static string [] GetLogicalDrivesInternal ();
845 [MethodImplAttribute (MethodImplOptions.InternalCall)]
846 private extern static string [] GetEnvironmentVariableNames ();
848 [MethodImplAttribute (MethodImplOptions.InternalCall)]
849 internal extern static string GetMachineConfigPath ();
851 [MethodImplAttribute (MethodImplOptions.InternalCall)]
852 internal extern static string internalGetHome ();
854 [MethodImplAttribute (MethodImplOptions.InternalCall)]
855 internal extern static int GetPageSize ();
857 static internal bool IsUnix {
859 int platform = (int) Environment.Platform;
861 return (platform == 4 || platform == 128 || platform == 6);