New tests.
[mono.git] / mcs / class / corlib / System / Environment.cs
1 //------------------------------------------------------------------------------
2 // 
3 // System.Environment.cs 
4 //
5 // Copyright (C) 2001 Moonlight Enterprises, All Rights Reserved
6 // 
7 // Author:         Jim Richardson, develop@wtfo-guru.com
8 //                 Dan Lewis (dihlewis@yahoo.co.uk)
9 // Created:        Saturday, August 11, 2001 
10 //
11 //------------------------------------------------------------------------------
12 //
13 // Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
14 //
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:
22 // 
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
25 // 
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.
33 //
34
35 using System.IO;
36 using System.Collections;
37 using System.Runtime.CompilerServices;
38 using System.Security;
39 using System.Security.Permissions;
40 using System.Text;
41 using System.Runtime.InteropServices;
42
43 namespace System {
44
45         [ComVisible (true)]
46         public static class Environment {
47
48                 /*
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.
56                  */
57 #pragma warning disable 169
58                 private const int mono_corlib_version = 90;
59 #pragma warning restore 169
60
61                 [ComVisible (true)]
62                 public enum SpecialFolder
63                 {       
64                         MyDocuments = 0x05,
65                         Desktop = 0x00,
66                         MyComputer = 0x11,
67                         Programs = 0x02,
68                         Personal = 0x05,
69                         Favorites = 0x06,
70                         Startup = 0x07,
71                         Recent = 0x08,
72                         SendTo = 0x09,
73                         StartMenu = 0x0b,
74                         MyMusic = 0x0d,
75                         DesktopDirectory = 0x10,
76                         Templates = 0x15,
77                         ApplicationData = 0x1a,
78                         LocalApplicationData = 0x1c,
79                         InternetCache = 0x20,
80                         Cookies = 0x21,
81                         History = 0x22,
82                         CommonApplicationData   = 0x23,
83                         System = 0x25,
84                         ProgramFiles = 0x26,
85                         MyPictures = 0x27,
86                         CommonProgramFiles = 0x2b,
87 #if NET_4_0 || MOONLIGHT
88                         MyVideos = 0x0e,
89 #endif
90 #if NET_4_0
91                         NetworkShortcuts = 0x13,
92                         Fonts = 0x14,
93                         CommonStartMenu = 0x16,
94                         CommonPrograms = 0x17,
95                         CommonStartup = 0x18,
96                         CommonDesktopDirectory = 0x19,
97                         PrinterShortcuts = 0x1b,
98                         Windows = 0x24,
99                         UserProfile = 0x28,
100                         SystemX86 = 0x29,
101                         ProgramFilesX86 = 0x2a,
102                         CommonProgramFilesX86 = 0x2c,
103                         CommonTemplates = 0x2d,
104                         CommonDocuments = 0x2e,
105                         CommonAdminTools = 0x2f,
106                         AdminTools = 0x30,
107                         CommonMusic = 0x35,
108                         CommonPictures = 0x36,
109                         CommonVideos = 0x37,
110                         Resources = 0x38,
111                         LocalizedResources = 0x39,
112                         CommonOemLinks = 0x3a,
113                         CDBurning = 0x3b,
114 #endif
115                 }
116
117 #if NET_4_0
118                 public
119 #endif
120                 enum SpecialFolderOption {
121                         None = 0,
122                         DoNotVerify = 0x4000,
123                         Create = 0x8000
124                 }
125
126                 /// <summary>
127                 /// Gets the command line for this process
128                 /// </summary>
129                 public static string CommandLine {
130                         // note: security demand inherited from calling GetCommandLineArgs
131                         get {
132                                 // FIXME: we may need to quote, but any sane person
133                                 // should use GetCommandLineArgs () instead.
134                                 return String.Join (" ", GetCommandLineArgs ());
135                         }
136                 }
137
138                 /// <summary>
139                 /// Gets or sets the current directory. Actually this is supposed to get
140                 /// and/or set the process start directory acording to the documentation
141                 /// but actually test revealed at beta2 it is just Getting/Setting the CurrentDirectory
142                 /// </summary>
143                 public static string CurrentDirectory
144                 {
145                         get {
146                                 return Directory.GetCurrentDirectory ();
147                         }
148                         set {
149                                 Directory.SetCurrentDirectory (value);
150                         }
151                 }
152
153                 /// <summary>
154                 /// Gets or sets the exit code of this process
155                 /// </summary>
156                 public extern static int ExitCode
157                 {       
158                         [MethodImplAttribute (MethodImplOptions.InternalCall)]
159                         get;
160                         [MethodImplAttribute (MethodImplOptions.InternalCall)]
161                         set;
162                 }
163
164                 static public extern bool HasShutdownStarted
165                 {
166                         [MethodImplAttribute (MethodImplOptions.InternalCall)]
167                         get;
168                 }
169                 
170
171                 /// <summary>
172                 /// Gets the name of the local computer
173                 /// </summary>
174                 public extern static string MachineName {
175                         [MethodImplAttribute (MethodImplOptions.InternalCall)]
176                         [EnvironmentPermission (SecurityAction.Demand, Read="COMPUTERNAME")]
177                         [SecurityPermission (SecurityAction.Demand, UnmanagedCode=true)]
178                         get;
179                 }
180
181                 /// <summary>
182                 /// Gets the standard new line value
183                 /// </summary>
184                 public extern static string NewLine {
185                         [MethodImplAttribute (MethodImplOptions.InternalCall)]
186                         get;
187                 }
188
189                 //
190                 // Support methods and fields for OSVersion property
191                 //
192                 static OperatingSystem os;
193
194                 internal static extern PlatformID Platform {
195                         [MethodImplAttribute (MethodImplOptions.InternalCall)]
196                         get;
197                 }
198
199                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
200                 internal static extern string GetOSVersionString ();
201
202                 /// <summary>
203                 /// Gets the current OS version information
204                 /// </summary>
205                 public static OperatingSystem OSVersion {
206                         get {
207                                 if (os == null) {
208                                         Version v = Version.CreateFromString (GetOSVersionString ());
209                                         PlatformID p = Platform;
210                                         os = new OperatingSystem (p, v);
211                                 }
212                                 return os;
213                         }
214                 }
215
216                 /// <summary>
217                 /// Get StackTrace
218                 /// </summary>
219                 public static string StackTrace {
220                         [EnvironmentPermission (SecurityAction.Demand, Unrestricted=true)]
221                         get {
222                                 System.Diagnostics.StackTrace trace = new System.Diagnostics.StackTrace (0, true);
223                                 return trace.ToString ();
224                         }
225                 }
226 #if !NET_2_1
227                 /// <summary>
228                 /// Get a fully qualified path to the system directory
229                 /// </summary>
230                 public static string SystemDirectory {
231                         get {
232                                 return GetFolderPath (SpecialFolder.System);
233                         }
234                 }
235 #endif
236                 /// <summary>
237                 /// Get the number of milliseconds that have elapsed since the system was booted
238                 /// </summary>
239                 public extern static int TickCount {
240                         [MethodImplAttribute (MethodImplOptions.InternalCall)]
241                         get;
242                 }
243
244                 /// <summary>
245                 /// Get UserDomainName
246                 /// </summary>
247                 public static string UserDomainName {
248                         // FIXME: this variable doesn't exist (at least not on WinXP) - reported to MS as FDBK20562
249                         [EnvironmentPermission (SecurityAction.Demand, Read="USERDOMAINNAME")]
250                         get {
251                                 return MachineName;
252                         }
253                 }
254
255                 /// <summary>
256                 /// Gets a flag indicating whether the process is in interactive mode
257                 /// </summary>
258                 [MonoTODO ("Currently always returns false, regardless of interactive state")]
259                 public static bool UserInteractive {
260                         get {
261                                 return false;
262                         }
263                 }
264
265                 /// <summary>
266                 /// Get the user name of current process is running under
267                 /// </summary>
268                 public extern static string UserName {
269                         [MethodImplAttribute (MethodImplOptions.InternalCall)]
270                         [EnvironmentPermission (SecurityAction.Demand, Read="USERNAME;USER")]
271                         get;
272                 }
273
274                 /// <summary>
275                 /// Get the version of the common language runtime 
276                 /// </summary>
277                 public static Version Version {
278                         get {
279                                 return new Version (Consts.FxFileVersion);
280                         }
281                 }
282
283                 /// <summary>
284                 /// Get the amount of physical memory mapped to process
285                 /// </summary>
286                 [MonoTODO ("Currently always returns zero")]
287                 public static long WorkingSet {
288                         [EnvironmentPermission (SecurityAction.Demand, Unrestricted=true)]
289                         get { return 0; }
290                 }
291
292                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
293                 [SecurityPermission (SecurityAction.Demand, UnmanagedCode=true)]
294                 public extern static void Exit (int exitCode);
295
296                 /// <summary>
297                 /// Substitute environment variables in the argument "name"
298                 /// </summary>
299                 public static string ExpandEnvironmentVariables (string name)
300                 {
301                         if (name == null)
302                                 throw new ArgumentNullException ("name");
303
304                         int off1 = name.IndexOf ('%');
305                         if (off1 == -1)
306                                 return name;
307
308                         int len = name.Length;
309                         int off2 = 0;
310                         if (off1 == len - 1 || (off2 = name.IndexOf ('%', off1 + 1)) == -1)
311                                 return name;
312
313                         StringBuilder result = new StringBuilder ();
314                         result.Append (name, 0, off1);
315                         Hashtable tbl = null;
316                         do {
317                                 string var = name.Substring (off1 + 1, off2 - off1 - 1);
318                                 string value = GetEnvironmentVariable (var);
319                                 if (value == null && Environment.IsRunningOnWindows) {
320                                         // On windows, env. vars. are case insensitive
321                                         if (tbl == null)
322                                                 tbl = GetEnvironmentVariablesNoCase ();
323
324                                         value = tbl [var] as string;
325                                 }
326                                 
327                                 // If value not found, add %FOO to stream,
328                                 //  and use the closing % for the next iteration.
329                                 // If value found, expand it in place of %FOO%
330                                 if (value == null) {
331                                         result.Append ('%');
332                                         result.Append (var);
333                                         off2--;
334                                 } else {
335                                         result.Append (value);
336                                 }
337                                 int oldOff2 = off2;
338                                 off1 = name.IndexOf ('%', off2 + 1);
339                                 // If no % found for off1, don't look for one for off2
340                                 off2 = (off1 == -1 || off2 > len-1)? -1 :name.IndexOf ('%', off1 + 1);
341                                 // textLen is the length of text between the closing % of current iteration
342                                 //  and the starting % of the next iteration if any. This text is added to output
343                                 int textLen;
344                                 // If no new % found, use all the remaining text
345                                 if (off1 == -1 || off2 == -1)
346                                         textLen = len - oldOff2 - 1;
347                                 // If value found in current iteration, use text after current closing % and next %
348                                 else if(value != null)
349                                         textLen = off1 - oldOff2 - 1;
350                                 // If value not found in current iteration, but a % was found for next iteration,
351                                 //  use text from current closing % to the next %.
352                                 else
353                                         textLen = off1 - oldOff2;
354                                 if(off1 >= oldOff2 || off1 == -1)
355                                         result.Append (name, oldOff2+1, textLen);
356                         } while (off2 > -1 && off2 < len);
357                                 
358                         return result.ToString ();
359
360                 }
361
362                 /// <summary>
363                 /// Return an array of the command line arguments of the current process
364                 /// </summary>
365                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
366                 [EnvironmentPermissionAttribute (SecurityAction.Demand, Read = "PATH")]
367                 public extern static string[] GetCommandLineArgs ();
368
369                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
370                 internal extern static string internalGetEnvironmentVariable (string variable);
371
372                 /// <summary>
373                 /// Return a string containing the value of the environment
374                 /// variable identifed by parameter "variable"
375                 /// </summary>
376                 public static string GetEnvironmentVariable (string variable)
377                 {
378 #if !NET_2_1
379                         if (SecurityManager.SecurityEnabled) {
380                                 new EnvironmentPermission (EnvironmentPermissionAccess.Read, variable).Demand ();
381                         }
382 #endif
383                         return internalGetEnvironmentVariable (variable);
384                 }
385
386                 static Hashtable GetEnvironmentVariablesNoCase ()
387                 {
388                         Hashtable vars = new Hashtable (CaseInsensitiveHashCodeProvider.Default,
389                                                         CaseInsensitiveComparer.Default);
390
391                         foreach (string name in GetEnvironmentVariableNames ()) {
392                                 vars [name] = internalGetEnvironmentVariable (name);
393                         }
394
395                         return vars;
396                 }
397
398                 /// <summary>
399                 /// Return a set of all environment variables and their values
400                 /// </summary>
401 #if !NET_2_1
402                 public static IDictionary GetEnvironmentVariables ()
403                 {
404                         StringBuilder sb = null;
405                         if (SecurityManager.SecurityEnabled) {
406                                 // we must have access to each variable to get the lot
407                                 sb = new StringBuilder ();
408                                 // but (performance-wise) we do not want a stack-walk
409                                 // for each of them so we concatenate them
410                         }
411
412                         Hashtable vars = new Hashtable ();
413                         foreach (string name in GetEnvironmentVariableNames ()) {
414                                 vars [name] = internalGetEnvironmentVariable (name);
415                                 if (sb != null) {
416                                         sb.Append (name);
417                                         sb.Append (";");
418                                 }
419                         }
420
421                         if (sb != null) {
422                                 new EnvironmentPermission (EnvironmentPermissionAccess.Read, sb.ToString ()).Demand ();
423                         }
424                         return vars;
425                 }
426 #else
427                 [EnvironmentPermission (SecurityAction.Demand, Unrestricted=true)]
428                 public static IDictionary GetEnvironmentVariables ()
429                 {
430                         Hashtable vars = new Hashtable ();
431                         foreach (string name in GetEnvironmentVariableNames ()) {
432                                 vars [name] = internalGetEnvironmentVariable (name);
433                         }
434                         return vars;
435                 }
436 #endif
437
438                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
439                 private extern static string GetWindowsFolderPath (int folder);
440
441                 /// <summary>
442                 /// Returns the fully qualified path of the
443                 /// folder specified by the "folder" parameter
444                 /// </summary>
445                 public static string GetFolderPath (SpecialFolder folder)
446                 {
447                         return GetFolderPath (folder, SpecialFolderOption.None);
448                 }
449 #if NET_4_0
450                 [MonoTODO ("Figure out the folder path for all the new values in SpecialFolder. Use the 'option' argument.")]
451                 public
452 #endif
453                 static string GetFolderPath(SpecialFolder folder, SpecialFolderOption option)
454                 {
455                         SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
456
457                         string dir = null;
458
459                         if (Environment.IsRunningOnWindows) {
460                                 dir = GetWindowsFolderPath ((int) folder);
461                         } else {
462                                 dir = InternalGetFolderPath (folder);
463                         }
464 #if !NET_2_1
465                         if ((dir != null) && (dir.Length > 0) && SecurityManager.SecurityEnabled) {
466                                 new FileIOPermission (FileIOPermissionAccess.PathDiscovery, dir).Demand ();
467                         }
468 #endif
469                         return dir;
470                 }
471
472                 private static string ReadXdgUserDir (string config_dir, string home_dir, 
473                         string key, string fallback)
474                 {
475                         string env_path = internalGetEnvironmentVariable (key);
476                         if (env_path != null && env_path != String.Empty) {
477                                 return env_path;
478                         }
479
480                         string user_dirs_path = Path.Combine (config_dir, "user-dirs.dirs");
481
482                         if (!File.Exists (user_dirs_path)) {
483                                 return Path.Combine (home_dir, fallback);
484                         }
485
486                         try {
487                                 using(StreamReader reader = new StreamReader (user_dirs_path)) {
488                                         string line;
489                                         while ((line = reader.ReadLine ()) != null) {
490                                                 line = line.Trim ();
491                                                 int delim_index = line.IndexOf ('=');
492                         if(delim_index > 8 && line.Substring (0, delim_index) == key) {
493                             string path = line.Substring (delim_index + 1).Trim ('"');
494                             bool relative = false;
495
496                             if (path.StartsWith ("$HOME/")) {
497                                 relative = true;
498                                 path = path.Substring (6);
499                             } else if (!path.StartsWith ("/")) {
500                                 relative = true;
501                             }
502
503                             return relative ? Path.Combine (home_dir, path) : path;
504                         }
505                                         }
506                                 }
507                         } catch (FileNotFoundException) {
508                         }
509
510                         return Path.Combine (home_dir, fallback);
511                 }
512
513
514                 // the security runtime (and maybe other parts of corlib) needs the
515                 // information to initialize themselves before permissions can be checked
516                 internal static string InternalGetFolderPath (SpecialFolder folder)
517                 {
518                         string home = internalGetHome ();
519
520                         // http://freedesktop.org/Standards/basedir-spec/basedir-spec-0.6.html
521
522                         // note: skip security check for environment variables
523                         string data = internalGetEnvironmentVariable ("XDG_DATA_HOME");
524                         if ((data == null) || (data == String.Empty)) {
525                                 data = Path.Combine (home, ".local");
526                                 data = Path.Combine (data, "share");
527                         }
528
529                         // note: skip security check for environment variables
530                         string config = internalGetEnvironmentVariable ("XDG_CONFIG_HOME");
531                         if ((config == null) || (config == String.Empty)) {
532                                 config = Path.Combine (home, ".config");
533                         }
534
535                         switch (folder) {
536                         // MyComputer is a virtual directory
537                         case SpecialFolder.MyComputer:
538                                 return String.Empty;
539
540                         // personal == ~
541                         case SpecialFolder.Personal:
542 #if MONOTOUCH
543                                 return Path.Combine (home, "Documents");
544 #else
545                                 return home;
546 #endif
547                         // use FDO's CONFIG_HOME. This data will be synced across a network like the windows counterpart.
548                         case SpecialFolder.ApplicationData:
549                                 return config;
550                         //use FDO's DATA_HOME. This is *NOT* synced
551                         case SpecialFolder.LocalApplicationData:
552                                 return data;
553                         case SpecialFolder.Desktop:
554                         case SpecialFolder.DesktopDirectory:
555                                 return ReadXdgUserDir (config, home, "XDG_DESKTOP_DIR", "Desktop");
556
557                         case SpecialFolder.MyMusic:
558                                 return ReadXdgUserDir (config, home, "XDG_MUSIC_DIR", "Music");
559
560                         case SpecialFolder.MyPictures:
561                                 return ReadXdgUserDir (config, home, "XDG_PICTURES_DIR", "Pictures");
562                                 
563                         // these simply dont exist on Linux
564                         // The spec says if a folder doesnt exist, we
565                         // should return ""
566                         case SpecialFolder.Favorites:
567                         case SpecialFolder.Programs:
568                         case SpecialFolder.SendTo:
569                         case SpecialFolder.StartMenu:
570                         case SpecialFolder.Startup:
571                         case SpecialFolder.Templates:
572                         case SpecialFolder.Cookies:
573                         case SpecialFolder.History:
574                         case SpecialFolder.InternetCache:
575                         case SpecialFolder.Recent:
576                         case SpecialFolder.CommonProgramFiles:
577                         case SpecialFolder.ProgramFiles:
578                         case SpecialFolder.System:
579                                 return String.Empty;
580                         // This is where data common to all users goes
581                         case SpecialFolder.CommonApplicationData:
582                                 return "/usr/share";
583                         default:
584                                 throw new ArgumentException ("Invalid SpecialFolder");
585                         }
586                 }
587
588                 [EnvironmentPermission (SecurityAction.Demand, Unrestricted=true)]
589                 public static string[] GetLogicalDrives ()
590                 {
591                         return GetLogicalDrivesInternal ();
592                 }
593
594 #if !NET_2_1
595                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
596                 private static extern void internalBroadcastSettingChange ();
597
598                 public static string GetEnvironmentVariable (string variable, EnvironmentVariableTarget target)
599                 {
600                         switch (target) {
601                         case EnvironmentVariableTarget.Process:
602                                 return GetEnvironmentVariable (variable);
603                         case EnvironmentVariableTarget.Machine:
604                                 new EnvironmentPermission (PermissionState.Unrestricted).Demand ();
605                                 if (!IsRunningOnWindows)
606                                         return null;
607                                 using (Microsoft.Win32.RegistryKey env = Microsoft.Win32.Registry.LocalMachine.OpenSubKey (@"SYSTEM\CurrentControlSet\Control\Session Manager\Environment")) {
608                                         object regvalue = env.GetValue (variable);
609                                         return (regvalue == null) ? null : regvalue.ToString ();
610                                 }
611                         case EnvironmentVariableTarget.User:
612                                 new EnvironmentPermission (PermissionState.Unrestricted).Demand ();
613                                 if (!IsRunningOnWindows)
614                                         return null;
615                                 using (Microsoft.Win32.RegistryKey env = Microsoft.Win32.Registry.CurrentUser.OpenSubKey ("Environment", false)) {
616                                         object regvalue = env.GetValue (variable);
617                                         return (regvalue == null) ? null : regvalue.ToString ();
618                                 }
619                         default:
620                                 throw new ArgumentException ("target");
621                         }
622                 }
623
624                 public static IDictionary GetEnvironmentVariables (EnvironmentVariableTarget target)
625                 {
626                         IDictionary variables = (IDictionary)new Hashtable ();
627                         switch (target) {
628                         case EnvironmentVariableTarget.Process:
629                                 variables = GetEnvironmentVariables ();
630                                 break;
631                         case EnvironmentVariableTarget.Machine:
632                                 new EnvironmentPermission (PermissionState.Unrestricted).Demand ();
633                                 if (IsRunningOnWindows) {
634                                         using (Microsoft.Win32.RegistryKey env = Microsoft.Win32.Registry.LocalMachine.OpenSubKey (@"SYSTEM\CurrentControlSet\Control\Session Manager\Environment")) {
635                                                 string[] value_names = env.GetValueNames ();
636                                                 foreach (string value_name in value_names)
637                                                         variables.Add (value_name, env.GetValue (value_name));
638                                         }
639                                 }
640                                 break;
641                         case EnvironmentVariableTarget.User:
642                                 new EnvironmentPermission (PermissionState.Unrestricted).Demand ();
643                                 if (IsRunningOnWindows) {
644                                         using (Microsoft.Win32.RegistryKey env = Microsoft.Win32.Registry.CurrentUser.OpenSubKey ("Environment")) {
645                                                 string[] value_names = env.GetValueNames ();
646                                                 foreach (string value_name in value_names)
647                                                         variables.Add (value_name, env.GetValue (value_name));
648                                         }
649                                 }
650                                 break;
651                         default:
652                                 throw new ArgumentException ("target");
653                         }
654                         return variables;
655                 }
656
657                 [EnvironmentPermission (SecurityAction.Demand, Unrestricted=true)]
658                 public static void SetEnvironmentVariable (string variable, string value)
659                 {
660                         SetEnvironmentVariable (variable, value, EnvironmentVariableTarget.Process);
661                 }
662
663                 [EnvironmentPermission (SecurityAction.Demand, Unrestricted = true)]
664                 public static void SetEnvironmentVariable (string variable, string value, EnvironmentVariableTarget target)
665                 {
666                         if (variable == null)
667                                 throw new ArgumentNullException ("variable");
668                         if (variable == String.Empty)
669                                 throw new ArgumentException ("String cannot be of zero length.", "variable");
670                         if (variable.IndexOf ('=') != -1)
671                                 throw new ArgumentException ("Environment variable name cannot contain an equal character.", "variable");
672                         if (variable[0] == '\0')
673                                 throw new ArgumentException ("The first char in the string is the null character.", "variable");
674
675                         switch (target) {
676                         case EnvironmentVariableTarget.Process:
677                                 InternalSetEnvironmentVariable (variable, value);
678                                 break;
679                         case EnvironmentVariableTarget.Machine:
680                                 if (!IsRunningOnWindows)
681                                         return;
682                                 using (Microsoft.Win32.RegistryKey env = Microsoft.Win32.Registry.LocalMachine.OpenSubKey (@"SYSTEM\CurrentControlSet\Control\Session Manager\Environment", true)) {
683                                         if (String.IsNullOrEmpty (value))
684                                                 env.DeleteValue (variable, false);
685                                         else
686                                                 env.SetValue (variable, value);
687                                         internalBroadcastSettingChange ();
688                                 }
689                                 break;
690                         case EnvironmentVariableTarget.User:
691                                 if (!IsRunningOnWindows)
692                                         return;
693                                 using (Microsoft.Win32.RegistryKey env = Microsoft.Win32.Registry.CurrentUser.OpenSubKey ("Environment", true)) {
694                                         if (String.IsNullOrEmpty (value))
695                                                 env.DeleteValue (variable, false);
696                                         else
697                                                 env.SetValue (variable, value);
698                                         internalBroadcastSettingChange ();
699                                 }
700                                 break;
701                         default:
702                                 throw new ArgumentException ("target");
703                         }
704                 }
705
706                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
707                 internal static extern void InternalSetEnvironmentVariable (string variable, string value);
708 #endif
709                 [SecurityPermission (SecurityAction.LinkDemand, UnmanagedCode=true)]
710                 public static void FailFast (string message)
711                 {
712                         throw new NotImplementedException ();
713                 }
714
715 #if NET_4_0 || MOONLIGHT
716                 [SecurityCritical]
717                 public static void FailFast (string message, Exception exception)
718                 {
719                         throw new NotImplementedException ();
720                 }
721 #endif
722
723 #if NET_4_0
724                 public static bool Is64BitOperatingSystem {
725                         get { return IntPtr.Size == 8; } // FIXME: is this good enough?
726                 }
727
728                 public static bool Is64BitProcess {
729                         get { return Is64BitOperatingSystem; }
730                 }
731
732                 public static int SystemPageSize {
733                         get { return GetPageSize (); }
734                 }
735 #endif
736
737                 public static extern int ProcessorCount {
738                         [EnvironmentPermission (SecurityAction.Demand, Read="NUMBER_OF_PROCESSORS")]
739                         [MethodImplAttribute (MethodImplOptions.InternalCall)]
740                         get;                    
741                 }
742
743                 // private methods
744
745                 internal static bool IsRunningOnWindows {
746                         get { return ((int) Platform < 4); }
747                 }
748 #if !NET_2_1
749                 //
750                 // Used by gacutil.exe
751                 //
752 #pragma warning disable 169             
753                 private static string GacPath {
754                         get {
755                                 if (Environment.IsRunningOnWindows) {
756                                         /* On windows, we don't know the path where mscorlib.dll will be installed */
757                                         string corlibDir = new DirectoryInfo (Path.GetDirectoryName (typeof (int).Assembly.Location)).Parent.Parent.FullName;
758                                         return Path.Combine (Path.Combine (corlibDir, "mono"), "gac");
759                                 }
760
761                                 return Path.Combine (Path.Combine (internalGetGacPath (), "mono"), "gac");
762                         }
763                 }
764 #pragma warning restore 169
765                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
766                 internal extern static string internalGetGacPath ();
767 #endif
768                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
769                 private extern static string [] GetLogicalDrivesInternal ();
770
771                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
772                 private extern static string [] GetEnvironmentVariableNames ();
773
774                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
775                 internal extern static string GetMachineConfigPath ();
776
777                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
778                 internal extern static string internalGetHome ();
779
780                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
781                 internal extern static int GetPageSize ();
782
783                 static internal bool IsUnix {
784                         get {
785                                 int platform = (int) Environment.Platform;
786
787                                 return (platform == 4 || platform == 128 || platform == 6);
788                         }
789                 }
790         }
791 }
792