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