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 = 89;
59 #pragma warning restore 169
62 public enum SpecialFolder
75 DesktopDirectory = 0x10,
77 ApplicationData = 0x1a,
78 LocalApplicationData = 0x1c,
82 CommonApplicationData = 0x23,
86 CommonProgramFiles = 0x2b,
89 NetworkShortcuts = 0x13,
91 CommonStartMenu = 0x16,
92 CommonPrograms = 0x17,
94 CommonDesktopDirectory = 0x19,
95 PrinterShortcuts = 0x1b,
99 ProgramFilesX86 = 0x2a,
100 CommonProgramFilesX86 = 0x2c,
101 CommonTemplates = 0x2d,
102 CommonDocuments = 0x2e,
103 CommonAdminTools = 0x2f,
106 CommonPictures = 0x36,
109 LocalizedResources = 0x39,
110 CommonOemLinks = 0x3a,
118 enum SpecialFolderOption {
120 DoNotVerify = 0x4000,
125 /// Gets the command line for this process
127 public static string CommandLine {
128 // note: security demand inherited from calling GetCommandLineArgs
130 // FIXME: we may need to quote, but any sane person
131 // should use GetCommandLineArgs () instead.
132 return String.Join (" ", GetCommandLineArgs ());
137 /// Gets or sets the current directory. Actually this is supposed to get
138 /// and/or set the process start directory acording to the documentation
139 /// but actually test revealed at beta2 it is just Getting/Setting the CurrentDirectory
141 public static string CurrentDirectory
144 return Directory.GetCurrentDirectory ();
147 Directory.SetCurrentDirectory (value);
152 /// Gets or sets the exit code of this process
154 public extern static int ExitCode
156 [MethodImplAttribute (MethodImplOptions.InternalCall)]
158 [MethodImplAttribute (MethodImplOptions.InternalCall)]
162 static public extern bool HasShutdownStarted
164 [MethodImplAttribute (MethodImplOptions.InternalCall)]
170 /// Gets the name of the local computer
172 public extern static string MachineName {
173 [MethodImplAttribute (MethodImplOptions.InternalCall)]
174 [EnvironmentPermission (SecurityAction.Demand, Read="COMPUTERNAME")]
175 [SecurityPermission (SecurityAction.Demand, UnmanagedCode=true)]
180 /// Gets the standard new line value
182 public extern static string NewLine {
183 [MethodImplAttribute (MethodImplOptions.InternalCall)]
188 // Support methods and fields for OSVersion property
190 static OperatingSystem os;
192 internal static extern PlatformID Platform {
193 [MethodImplAttribute (MethodImplOptions.InternalCall)]
197 [MethodImplAttribute (MethodImplOptions.InternalCall)]
198 internal static extern string GetOSVersionString ();
201 /// Gets the current OS version information
203 public static OperatingSystem OSVersion {
206 Version v = Version.CreateFromString (GetOSVersionString ());
207 PlatformID p = Platform;
208 os = new OperatingSystem (p, v);
217 public static string StackTrace {
218 [EnvironmentPermission (SecurityAction.Demand, Unrestricted=true)]
220 System.Diagnostics.StackTrace trace = new System.Diagnostics.StackTrace (0, true);
221 return trace.ToString ();
226 /// Get a fully qualified path to the system directory
228 public static string SystemDirectory {
230 return GetFolderPath (SpecialFolder.System);
235 /// Get the number of milliseconds that have elapsed since the system was booted
237 public extern static int TickCount {
238 [MethodImplAttribute (MethodImplOptions.InternalCall)]
243 /// Get UserDomainName
245 public static string UserDomainName {
246 // FIXME: this variable doesn't exist (at least not on WinXP) - reported to MS as FDBK20562
247 [EnvironmentPermission (SecurityAction.Demand, Read="USERDOMAINNAME")]
254 /// Gets a flag indicating whether the process is in interactive mode
256 [MonoTODO ("Currently always returns false, regardless of interactive state")]
257 public static bool UserInteractive {
264 /// Get the user name of current process is running under
266 public extern static string UserName {
267 [MethodImplAttribute (MethodImplOptions.InternalCall)]
268 [EnvironmentPermission (SecurityAction.Demand, Read="USERNAME;USER")]
273 /// Get the version of the common language runtime
275 public static Version Version {
277 return new Version (Consts.FxFileVersion);
282 /// Get the amount of physical memory mapped to process
284 [MonoTODO ("Currently always returns zero")]
285 public static long WorkingSet {
286 [EnvironmentPermission (SecurityAction.Demand, Unrestricted=true)]
290 [MethodImplAttribute (MethodImplOptions.InternalCall)]
291 [SecurityPermission (SecurityAction.Demand, UnmanagedCode=true)]
292 public extern static void Exit (int exitCode);
295 /// Substitute environment variables in the argument "name"
297 public static string ExpandEnvironmentVariables (string name)
300 throw new ArgumentNullException ("name");
302 int off1 = name.IndexOf ('%');
306 int len = name.Length;
308 if (off1 == len - 1 || (off2 = name.IndexOf ('%', off1 + 1)) == -1)
311 StringBuilder result = new StringBuilder ();
312 result.Append (name, 0, off1);
313 Hashtable tbl = null;
315 string var = name.Substring (off1 + 1, off2 - off1 - 1);
316 string value = GetEnvironmentVariable (var);
317 if (value == null && Environment.IsRunningOnWindows) {
318 // On windows, env. vars. are case insensitive
320 tbl = GetEnvironmentVariablesNoCase ();
322 value = tbl [var] as string;
325 // If value not found, add %FOO to stream,
326 // and use the closing % for the next iteration.
327 // If value found, expand it in place of %FOO%
333 result.Append (value);
336 off1 = name.IndexOf ('%', off2 + 1);
337 // If no % found for off1, don't look for one for off2
338 off2 = (off1 == -1 || off2 > len-1)? -1 :name.IndexOf ('%', off1 + 1);
339 // textLen is the length of text between the closing % of current iteration
340 // and the starting % of the next iteration if any. This text is added to output
342 // If no new % found, use all the remaining text
343 if (off1 == -1 || off2 == -1)
344 textLen = len - oldOff2 - 1;
345 // If value found in current iteration, use text after current closing % and next %
346 else if(value != null)
347 textLen = off1 - oldOff2 - 1;
348 // If value not found in current iteration, but a % was found for next iteration,
349 // use text from current closing % to the next %.
351 textLen = off1 - oldOff2;
352 if(off1 >= oldOff2 || off1 == -1)
353 result.Append (name, oldOff2+1, textLen);
354 } while (off2 > -1 && off2 < len);
356 return result.ToString ();
361 /// Return an array of the command line arguments of the current process
363 [MethodImplAttribute (MethodImplOptions.InternalCall)]
364 [EnvironmentPermissionAttribute (SecurityAction.Demand, Read = "PATH")]
365 public extern static string[] GetCommandLineArgs ();
367 [MethodImplAttribute (MethodImplOptions.InternalCall)]
368 internal extern static string internalGetEnvironmentVariable (string variable);
371 /// Return a string containing the value of the environment
372 /// variable identifed by parameter "variable"
374 public static string GetEnvironmentVariable (string variable)
377 if (SecurityManager.SecurityEnabled) {
378 new EnvironmentPermission (EnvironmentPermissionAccess.Read, variable).Demand ();
381 return internalGetEnvironmentVariable (variable);
384 static Hashtable GetEnvironmentVariablesNoCase ()
386 Hashtable vars = new Hashtable (CaseInsensitiveHashCodeProvider.Default,
387 CaseInsensitiveComparer.Default);
389 foreach (string name in GetEnvironmentVariableNames ()) {
390 vars [name] = internalGetEnvironmentVariable (name);
397 /// Return a set of all environment variables and their values
400 public static IDictionary GetEnvironmentVariables ()
402 StringBuilder sb = null;
403 if (SecurityManager.SecurityEnabled) {
404 // we must have access to each variable to get the lot
405 sb = new StringBuilder ();
406 // but (performance-wise) we do not want a stack-walk
407 // for each of them so we concatenate them
410 Hashtable vars = new Hashtable ();
411 foreach (string name in GetEnvironmentVariableNames ()) {
412 vars [name] = internalGetEnvironmentVariable (name);
420 new EnvironmentPermission (EnvironmentPermissionAccess.Read, sb.ToString ()).Demand ();
425 [EnvironmentPermission (SecurityAction.Demand, Unrestricted=true)]
426 public static IDictionary GetEnvironmentVariables ()
428 Hashtable vars = new Hashtable ();
429 foreach (string name in GetEnvironmentVariableNames ()) {
430 vars [name] = internalGetEnvironmentVariable (name);
436 [MethodImplAttribute (MethodImplOptions.InternalCall)]
437 private extern static string GetWindowsFolderPath (int folder);
440 /// Returns the fully qualified path of the
441 /// folder specified by the "folder" parameter
443 public static string GetFolderPath (SpecialFolder folder)
445 return GetFolderPath (folder, SpecialFolderOption.None);
448 [MonoTODO ("Figure out the folder path for all the new values in SpecialFolder. Use the 'option' argument.")]
451 static string GetFolderPath(SpecialFolder folder, SpecialFolderOption option)
455 if (Environment.IsRunningOnWindows) {
456 dir = GetWindowsFolderPath ((int) folder);
458 dir = InternalGetFolderPath (folder);
461 if ((dir != null) && (dir.Length > 0) && SecurityManager.SecurityEnabled) {
462 new FileIOPermission (FileIOPermissionAccess.PathDiscovery, dir).Demand ();
468 private static string ReadXdgUserDir (string config_dir, string home_dir,
469 string key, string fallback)
471 string env_path = internalGetEnvironmentVariable (key);
472 if (env_path != null && env_path != String.Empty) {
476 string user_dirs_path = Path.Combine (config_dir, "user-dirs.dirs");
478 if (!File.Exists (user_dirs_path)) {
479 return Path.Combine (home_dir, fallback);
483 using(StreamReader reader = new StreamReader (user_dirs_path)) {
485 while ((line = reader.ReadLine ()) != null) {
487 int delim_index = line.IndexOf ('=');
488 if(delim_index > 8 && line.Substring (0, delim_index) == key) {
489 string path = line.Substring (delim_index + 1).Trim ('"');
490 bool relative = false;
492 if (path.StartsWith ("$HOME/")) {
494 path = path.Substring (6);
495 } else if (!path.StartsWith ("/")) {
499 return relative ? Path.Combine (home_dir, path) : path;
503 } catch (FileNotFoundException) {
506 return Path.Combine (home_dir, fallback);
510 // the security runtime (and maybe other parts of corlib) needs the
511 // information to initialize themselves before permissions can be checked
512 internal static string InternalGetFolderPath (SpecialFolder folder)
514 string home = internalGetHome ();
516 // http://freedesktop.org/Standards/basedir-spec/basedir-spec-0.6.html
518 // note: skip security check for environment variables
519 string data = internalGetEnvironmentVariable ("XDG_DATA_HOME");
520 if ((data == null) || (data == String.Empty)) {
521 data = Path.Combine (home, ".local");
522 data = Path.Combine (data, "share");
525 // note: skip security check for environment variables
526 string config = internalGetEnvironmentVariable ("XDG_CONFIG_HOME");
527 if ((config == null) || (config == String.Empty)) {
528 config = Path.Combine (home, ".config");
532 // MyComputer is a virtual directory
533 case SpecialFolder.MyComputer:
537 case SpecialFolder.Personal:
539 return Path.Combine (home, "Documents");
543 // use FDO's CONFIG_HOME. This data will be synced across a network like the windows counterpart.
544 case SpecialFolder.ApplicationData:
546 //use FDO's DATA_HOME. This is *NOT* synced
547 case SpecialFolder.LocalApplicationData:
549 case SpecialFolder.Desktop:
550 case SpecialFolder.DesktopDirectory:
551 return ReadXdgUserDir (config, home, "XDG_DESKTOP_DIR", "Desktop");
553 case SpecialFolder.MyMusic:
554 return ReadXdgUserDir (config, home, "XDG_MUSIC_DIR", "Music");
556 case SpecialFolder.MyPictures:
557 return ReadXdgUserDir (config, home, "XDG_PICTURES_DIR", "Pictures");
559 // these simply dont exist on Linux
560 // The spec says if a folder doesnt exist, we
562 case SpecialFolder.Favorites:
563 case SpecialFolder.Programs:
564 case SpecialFolder.SendTo:
565 case SpecialFolder.StartMenu:
566 case SpecialFolder.Startup:
567 case SpecialFolder.Templates:
568 case SpecialFolder.Cookies:
569 case SpecialFolder.History:
570 case SpecialFolder.InternetCache:
571 case SpecialFolder.Recent:
572 case SpecialFolder.CommonProgramFiles:
573 case SpecialFolder.ProgramFiles:
574 case SpecialFolder.System:
576 // This is where data common to all users goes
577 case SpecialFolder.CommonApplicationData:
580 throw new ArgumentException ("Invalid SpecialFolder");
584 [EnvironmentPermission (SecurityAction.Demand, Unrestricted=true)]
585 public static string[] GetLogicalDrives ()
587 return GetLogicalDrivesInternal ();
591 [MethodImplAttribute (MethodImplOptions.InternalCall)]
592 private static extern void internalBroadcastSettingChange ();
594 public static string GetEnvironmentVariable (string variable, EnvironmentVariableTarget target)
597 case EnvironmentVariableTarget.Process:
598 return GetEnvironmentVariable (variable);
599 case EnvironmentVariableTarget.Machine:
600 new EnvironmentPermission (PermissionState.Unrestricted).Demand ();
601 if (!IsRunningOnWindows)
603 using (Microsoft.Win32.RegistryKey env = Microsoft.Win32.Registry.LocalMachine.OpenSubKey (@"SYSTEM\CurrentControlSet\Control\Session Manager\Environment")) {
604 object regvalue = env.GetValue (variable);
605 return (regvalue == null) ? null : regvalue.ToString ();
607 case EnvironmentVariableTarget.User:
608 new EnvironmentPermission (PermissionState.Unrestricted).Demand ();
609 if (!IsRunningOnWindows)
611 using (Microsoft.Win32.RegistryKey env = Microsoft.Win32.Registry.CurrentUser.OpenSubKey ("Environment", false)) {
612 object regvalue = env.GetValue (variable);
613 return (regvalue == null) ? null : regvalue.ToString ();
616 throw new ArgumentException ("target");
620 public static IDictionary GetEnvironmentVariables (EnvironmentVariableTarget target)
622 IDictionary variables = (IDictionary)new Hashtable ();
624 case EnvironmentVariableTarget.Process:
625 variables = GetEnvironmentVariables ();
627 case EnvironmentVariableTarget.Machine:
628 new EnvironmentPermission (PermissionState.Unrestricted).Demand ();
629 if (IsRunningOnWindows) {
630 using (Microsoft.Win32.RegistryKey env = Microsoft.Win32.Registry.LocalMachine.OpenSubKey (@"SYSTEM\CurrentControlSet\Control\Session Manager\Environment")) {
631 string[] value_names = env.GetValueNames ();
632 foreach (string value_name in value_names)
633 variables.Add (value_name, env.GetValue (value_name));
637 case EnvironmentVariableTarget.User:
638 new EnvironmentPermission (PermissionState.Unrestricted).Demand ();
639 if (IsRunningOnWindows) {
640 using (Microsoft.Win32.RegistryKey env = Microsoft.Win32.Registry.CurrentUser.OpenSubKey ("Environment")) {
641 string[] value_names = env.GetValueNames ();
642 foreach (string value_name in value_names)
643 variables.Add (value_name, env.GetValue (value_name));
648 throw new ArgumentException ("target");
653 [EnvironmentPermission (SecurityAction.Demand, Unrestricted=true)]
654 public static void SetEnvironmentVariable (string variable, string value)
656 SetEnvironmentVariable (variable, value, EnvironmentVariableTarget.Process);
659 [EnvironmentPermission (SecurityAction.Demand, Unrestricted = true)]
660 public static void SetEnvironmentVariable (string variable, string value, EnvironmentVariableTarget target)
662 if (variable == null)
663 throw new ArgumentNullException ("variable");
664 if (variable == String.Empty)
665 throw new ArgumentException ("String cannot be of zero length.", "variable");
666 if (variable.IndexOf ('=') != -1)
667 throw new ArgumentException ("Environment variable name cannot contain an equal character.", "variable");
668 if (variable[0] == '\0')
669 throw new ArgumentException ("The first char in the string is the null character.", "variable");
672 case EnvironmentVariableTarget.Process:
673 InternalSetEnvironmentVariable (variable, value);
675 case EnvironmentVariableTarget.Machine:
676 if (!IsRunningOnWindows)
678 using (Microsoft.Win32.RegistryKey env = Microsoft.Win32.Registry.LocalMachine.OpenSubKey (@"SYSTEM\CurrentControlSet\Control\Session Manager\Environment", true)) {
679 if (String.IsNullOrEmpty (value))
680 env.DeleteValue (variable, false);
682 env.SetValue (variable, value);
683 internalBroadcastSettingChange ();
686 case EnvironmentVariableTarget.User:
687 if (!IsRunningOnWindows)
689 using (Microsoft.Win32.RegistryKey env = Microsoft.Win32.Registry.CurrentUser.OpenSubKey ("Environment", true)) {
690 if (String.IsNullOrEmpty (value))
691 env.DeleteValue (variable, false);
693 env.SetValue (variable, value);
694 internalBroadcastSettingChange ();
698 throw new ArgumentException ("target");
702 [MethodImplAttribute (MethodImplOptions.InternalCall)]
703 internal static extern void InternalSetEnvironmentVariable (string variable, string value);
705 [SecurityPermission (SecurityAction.LinkDemand, UnmanagedCode=true)]
706 public static void FailFast (string message)
708 throw new NotImplementedException ();
713 public static void FailFast (string message, Exception exception)
715 throw new NotImplementedException ();
718 public static bool Is64BitOperatingSystem {
719 get { return IntPtr.Size == 8; } // FIXME: is this good enough?
722 public static bool Is64BitProcess {
723 get { return Is64BitOperatingSystem; }
726 public static int SystemPageSize {
727 get { return GetPageSize (); }
731 public static extern int ProcessorCount {
732 [EnvironmentPermission (SecurityAction.Demand, Read="NUMBER_OF_PROCESSORS")]
733 [MethodImplAttribute (MethodImplOptions.InternalCall)]
739 internal static bool IsRunningOnWindows {
740 get { return ((int) Platform < 4); }
744 // Used by gacutil.exe
746 #pragma warning disable 169
747 private static string GacPath {
749 if (Environment.IsRunningOnWindows) {
750 /* On windows, we don't know the path where mscorlib.dll will be installed */
751 string corlibDir = new DirectoryInfo (Path.GetDirectoryName (typeof (int).Assembly.Location)).Parent.Parent.FullName;
752 return Path.Combine (Path.Combine (corlibDir, "mono"), "gac");
755 return Path.Combine (Path.Combine (internalGetGacPath (), "mono"), "gac");
758 #pragma warning restore 169
759 [MethodImplAttribute (MethodImplOptions.InternalCall)]
760 internal extern static string internalGetGacPath ();
762 [MethodImplAttribute (MethodImplOptions.InternalCall)]
763 private extern static string [] GetLogicalDrivesInternal ();
765 [MethodImplAttribute (MethodImplOptions.InternalCall)]
766 private extern static string [] GetEnvironmentVariableNames ();
768 [MethodImplAttribute (MethodImplOptions.InternalCall)]
769 internal extern static string GetMachineConfigPath ();
771 [MethodImplAttribute (MethodImplOptions.InternalCall)]
772 internal extern static string internalGetHome ();
774 [MethodImplAttribute (MethodImplOptions.InternalCall)]
775 internal extern static int GetPageSize ();
777 static internal bool IsUnix {
779 int platform = (int) Environment.Platform;
781 return (platform == 4 || platform == 128 || platform == 6);