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