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