1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
6 using System
.Collections
;
7 using System
.Diagnostics
;
8 using System
.Runtime
.InteropServices
;
12 public static partial class Environment
14 private static string? GetEnvironmentVariableCore(string variable
)
16 Span
<char> buffer
= stackalloc char[128]; // a somewhat reasonable default size
17 int requiredSize
= Interop
.Kernel32
.GetEnvironmentVariable(variable
, buffer
);
19 if (requiredSize
== 0 && Marshal
.GetLastWin32Error() == Interop
.Errors
.ERROR_ENVVAR_NOT_FOUND
)
24 if (requiredSize
<= buffer
.Length
)
26 return new string(buffer
.Slice(0, requiredSize
));
29 char[] chars
= ArrayPool
<char>.Shared
.Rent(requiredSize
);
33 requiredSize
= Interop
.Kernel32
.GetEnvironmentVariable(variable
, buffer
);
34 if ((requiredSize
== 0 && Marshal
.GetLastWin32Error() == Interop
.Errors
.ERROR_ENVVAR_NOT_FOUND
) ||
35 requiredSize
> buffer
.Length
)
40 return new string(buffer
.Slice(0, requiredSize
));
44 ArrayPool
<char>.Shared
.Return(chars
);
48 private static void SetEnvironmentVariableCore(string variable
, string? value)
50 if (!Interop
.Kernel32
.SetEnvironmentVariable(variable
, value))
52 int errorCode
= Marshal
.GetLastWin32Error();
55 case Interop
.Errors
.ERROR_ENVVAR_NOT_FOUND
:
56 // Allow user to try to clear a environment variable
59 case Interop
.Errors
.ERROR_FILENAME_EXCED_RANGE
:
60 // The error message from Win32 is "The filename or extension is too long",
61 // which is not accurate.
62 throw new ArgumentException(SR
.Argument_LongEnvVarValue
);
64 case Interop
.Errors
.ERROR_NOT_ENOUGH_MEMORY
:
65 case Interop
.Errors
.ERROR_NO_SYSTEM_RESOURCES
:
66 throw new OutOfMemoryException(Interop
.Kernel32
.GetMessage(errorCode
));
69 throw new ArgumentException(Interop
.Kernel32
.GetMessage(errorCode
));
74 public static unsafe IDictionary
GetEnvironmentVariables()
76 char* pStrings
= Interop
.Kernel32
.GetEnvironmentStrings();
79 throw new OutOfMemoryException();
84 // Format for GetEnvironmentStrings is:
85 // [=HiddenVar=value\0]* [Variable=value\0]* \0
86 // See the description of Environment Blocks in MSDN's
87 // CreateProcess page (null-terminated array of null-terminated strings).
89 // Search for terminating \0\0 (two unicode \0's).
91 while (!(*p
== '\0' && *(p
+ 1) == '\0'))
95 Span
<char> block
= new Span
<char>(pStrings
, (int)(p
- pStrings
+ 1));
97 // Format for GetEnvironmentStrings is:
98 // (=HiddenVar=value\0 | Variable=value\0)* \0
99 // See the description of Environment Blocks in MSDN's
100 // CreateProcess page (null-terminated array of null-terminated strings).
101 // Note the =HiddenVar's aren't always at the beginning.
103 // Copy strings out, parsing into pairs and inserting into the table.
104 // The first few environment variable entries start with an '='.
105 // The current working directory of every drive (except for those drives
106 // you haven't cd'ed into in your DOS window) are stored in the
107 // environment block (as =C:=pwd) and the program's exit code is
108 // as well (=ExitCode=00000000).
110 var results
= new Hashtable();
111 for (int i
= 0; i
< block
.Length
; i
++)
115 // Skip to key. On some old OS, the environment block can be corrupted.
116 // Some will not have '=', so we need to check for '\0'.
117 while (block
[i
] != '=' && block
[i
] != '\0')
122 if (block
[i
] == '\0')
127 // Skip over environment variables starting with '='
128 if (i
- startKey
== 0)
130 while (block
[i
] != 0)
138 string key
= new string(block
.Slice(startKey
, i
- startKey
));
139 i
++; // skip over '='
142 while (block
[i
] != 0)
144 i
++; // Read to end of this entry
147 string value = new string(block
.Slice(startValue
, i
- startValue
)); // skip over 0 handled by for loop's i++
150 results
.Add(key
, value);
152 catch (ArgumentException
)
154 // Throw and catch intentionally to provide non-fatal notification about corrupted environment block
161 bool success
= Interop
.Kernel32
.FreeEnvironmentStrings(pStrings
);
162 Debug
.Assert(success
);