Fix IDE0025 (use expression body for properties)
[mono-project.git] / netcore / System.Private.CoreLib / shared / System / Environment.Win32.cs
blob1ea246164bed01bb27f51c32ba53f40f478e7556
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.
5 using System.Collections;
6 using System.Diagnostics;
7 using System.IO;
8 using System.Reflection;
9 using System.Runtime.InteropServices;
10 using System.Text;
11 using Internal.Win32;
13 namespace System
15 public static partial class Environment
17 internal static bool IsWindows8OrAbove => WindowsVersion.IsWindows8OrAbove;
19 private static string? GetEnvironmentVariableFromRegistry(string variable, bool fromMachine)
21 Debug.Assert(variable != null);
23 #if FEATURE_APPX
24 if (ApplicationModel.IsUap)
25 return null; // Systems without the Windows registry pretend that it's always empty.
26 #endif
28 using (RegistryKey? environmentKey = OpenEnvironmentKeyIfExists(fromMachine, writable: false))
30 return environmentKey?.GetValue(variable) as string;
34 private static void SetEnvironmentVariableFromRegistry(string variable, string? value, bool fromMachine)
36 Debug.Assert(variable != null);
38 #if FEATURE_APPX
39 if (ApplicationModel.IsUap)
40 return; // Systems without the Windows registry pretend that it's always empty.
41 #endif
43 const int MaxUserEnvVariableLength = 255; // User-wide env vars stored in the registry have names limited to 255 chars
44 if (!fromMachine && variable.Length >= MaxUserEnvVariableLength)
46 throw new ArgumentException(SR.Argument_LongEnvVarValue, nameof(variable));
49 using (RegistryKey? environmentKey = OpenEnvironmentKeyIfExists(fromMachine, writable: true))
51 if (environmentKey != null)
53 if (value == null)
55 environmentKey.DeleteValue(variable, throwOnMissingValue: false);
57 else
59 environmentKey.SetValue(variable, value);
64 // send a WM_SETTINGCHANGE message to all windows
65 IntPtr r = Interop.User32.SendMessageTimeout(new IntPtr(Interop.User32.HWND_BROADCAST), Interop.User32.WM_SETTINGCHANGE, IntPtr.Zero, "Environment", 0, 1000, IntPtr.Zero);
66 Debug.Assert(r != IntPtr.Zero, "SetEnvironmentVariable failed: " + Marshal.GetLastWin32Error());
69 private static IDictionary GetEnvironmentVariablesFromRegistry(bool fromMachine)
71 var results = new Hashtable();
72 #if FEATURE_APPX
73 if (ApplicationModel.IsUap) // Systems without the Windows registry pretend that it's always empty.
74 return results;
75 #endif
77 using (RegistryKey? environmentKey = OpenEnvironmentKeyIfExists(fromMachine, writable: false))
79 if (environmentKey != null)
81 foreach (string name in environmentKey.GetValueNames())
83 string? value = environmentKey.GetValue(name, "").ToString();
84 try
86 results.Add(name, value);
88 catch (ArgumentException)
90 // Throw and catch intentionally to provide non-fatal notification about corrupted environment block
96 return results;
99 private static RegistryKey? OpenEnvironmentKeyIfExists(bool fromMachine, bool writable)
101 RegistryKey baseKey;
102 string keyName;
104 if (fromMachine)
106 baseKey = Registry.LocalMachine;
107 keyName = @"System\CurrentControlSet\Control\Session Manager\Environment";
109 else
111 baseKey = Registry.CurrentUser;
112 keyName = "Environment";
115 return baseKey.OpenSubKey(keyName, writable: writable);
118 public static string UserName
122 #if FEATURE_APPX
123 if (ApplicationModel.IsUap)
124 return "Windows User";
125 #endif
127 // 40 should be enough as we're asking for the SAM compatible name (DOMAIN\User).
128 // The max length should be 15 (domain) + 1 (separator) + 20 (name) + null. If for
129 // some reason it isn't, we'll grow the buffer.
131 // https://support.microsoft.com/en-us/help/909264/naming-conventions-in-active-directory-for-computers-domains-sites-and
132 // https://msdn.microsoft.com/en-us/library/ms679635.aspx
134 Span<char> initialBuffer = stackalloc char[40];
135 var builder = new ValueStringBuilder(initialBuffer);
136 GetUserName(ref builder);
138 ReadOnlySpan<char> name = builder.AsSpan();
139 int index = name.IndexOf('\\');
140 if (index != -1)
142 // In the form of DOMAIN\User, cut off DOMAIN\
143 name = name.Slice(index + 1);
146 return name.ToString();
150 private static void GetUserName(ref ValueStringBuilder builder)
152 uint size = 0;
153 while (Interop.Secur32.GetUserNameExW(Interop.Secur32.NameSamCompatible, ref builder.GetPinnableReference(), ref size) == Interop.BOOLEAN.FALSE)
155 if (Marshal.GetLastWin32Error() == Interop.Errors.ERROR_MORE_DATA)
157 builder.EnsureCapacity(checked((int)size));
159 else
161 builder.Length = 0;
162 return;
166 builder.Length = (int)size;
169 public static string UserDomainName
173 #if FEATURE_APPX
174 if (ApplicationModel.IsUap)
175 return "Windows Domain";
176 #endif
178 // See the comment in UserName
179 Span<char> initialBuffer = stackalloc char[40];
180 var builder = new ValueStringBuilder(initialBuffer);
181 GetUserName(ref builder);
183 ReadOnlySpan<char> name = builder.AsSpan();
184 int index = name.IndexOf('\\');
185 if (index != -1)
187 // In the form of DOMAIN\User, cut off \User and return
188 return name.Slice(0, index).ToString();
191 // In theory we should never get use out of LookupAccountNameW as the above API should
192 // always return what we need. Can't find any clues in the historical sources, however.
194 // Domain names aren't typically long.
195 // https://support.microsoft.com/en-us/help/909264/naming-conventions-in-active-directory-for-computers-domains-sites-and
196 Span<char> initialDomainNameBuffer = stackalloc char[64];
197 var domainBuilder = new ValueStringBuilder(initialBuffer);
198 uint length = (uint)domainBuilder.Capacity;
200 // This API will fail to return the domain name without a buffer for the SID.
201 // SIDs are never over 68 bytes long.
202 Span<byte> sid = stackalloc byte[68];
203 uint sidLength = 68;
205 while (!Interop.Advapi32.LookupAccountNameW(null, ref builder.GetPinnableReference(), ref MemoryMarshal.GetReference(sid),
206 ref sidLength, ref domainBuilder.GetPinnableReference(), ref length, out _))
208 int error = Marshal.GetLastWin32Error();
210 // The docs don't call this out clearly, but experimenting shows that the error returned is the following.
211 if (error != Interop.Errors.ERROR_INSUFFICIENT_BUFFER)
213 throw new InvalidOperationException(Win32Marshal.GetMessage(error));
216 domainBuilder.EnsureCapacity((int)length);
219 domainBuilder.Length = (int)length;
220 return domainBuilder.ToString();
224 private static string GetFolderPathCore(SpecialFolder folder, SpecialFolderOption option)
226 #if FEATURE_APPX
227 if (ApplicationModel.IsUap)
228 return WinRTFolderPaths.GetFolderPath(folder, option);
229 #endif
231 // We're using SHGetKnownFolderPath instead of SHGetFolderPath as SHGetFolderPath is
232 // capped at MAX_PATH.
234 // Because we validate both of the input enums we shouldn't have to care about CSIDL and flag
235 // definitions we haven't mapped. If we remove or loosen the checks we'd have to account
236 // for mapping here (this includes tweaking as SHGetFolderPath would do).
238 // The only SpecialFolderOption defines we have are equivalent to KnownFolderFlags.
240 string folderGuid;
242 switch (folder)
244 case SpecialFolder.ApplicationData:
245 folderGuid = Interop.Shell32.KnownFolders.RoamingAppData;
246 break;
247 case SpecialFolder.CommonApplicationData:
248 folderGuid = Interop.Shell32.KnownFolders.ProgramData;
249 break;
250 case SpecialFolder.LocalApplicationData:
251 folderGuid = Interop.Shell32.KnownFolders.LocalAppData;
252 break;
253 case SpecialFolder.Cookies:
254 folderGuid = Interop.Shell32.KnownFolders.Cookies;
255 break;
256 case SpecialFolder.Desktop:
257 folderGuid = Interop.Shell32.KnownFolders.Desktop;
258 break;
259 case SpecialFolder.Favorites:
260 folderGuid = Interop.Shell32.KnownFolders.Favorites;
261 break;
262 case SpecialFolder.History:
263 folderGuid = Interop.Shell32.KnownFolders.History;
264 break;
265 case SpecialFolder.InternetCache:
266 folderGuid = Interop.Shell32.KnownFolders.InternetCache;
267 break;
268 case SpecialFolder.Programs:
269 folderGuid = Interop.Shell32.KnownFolders.Programs;
270 break;
271 case SpecialFolder.MyComputer:
272 folderGuid = Interop.Shell32.KnownFolders.ComputerFolder;
273 break;
274 case SpecialFolder.MyMusic:
275 folderGuid = Interop.Shell32.KnownFolders.Music;
276 break;
277 case SpecialFolder.MyPictures:
278 folderGuid = Interop.Shell32.KnownFolders.Pictures;
279 break;
280 case SpecialFolder.MyVideos:
281 folderGuid = Interop.Shell32.KnownFolders.Videos;
282 break;
283 case SpecialFolder.Recent:
284 folderGuid = Interop.Shell32.KnownFolders.Recent;
285 break;
286 case SpecialFolder.SendTo:
287 folderGuid = Interop.Shell32.KnownFolders.SendTo;
288 break;
289 case SpecialFolder.StartMenu:
290 folderGuid = Interop.Shell32.KnownFolders.StartMenu;
291 break;
292 case SpecialFolder.Startup:
293 folderGuid = Interop.Shell32.KnownFolders.Startup;
294 break;
295 case SpecialFolder.System:
296 folderGuid = Interop.Shell32.KnownFolders.System;
297 break;
298 case SpecialFolder.Templates:
299 folderGuid = Interop.Shell32.KnownFolders.Templates;
300 break;
301 case SpecialFolder.DesktopDirectory:
302 folderGuid = Interop.Shell32.KnownFolders.Desktop;
303 break;
304 case SpecialFolder.Personal:
305 // Same as Personal
306 // case SpecialFolder.MyDocuments:
307 folderGuid = Interop.Shell32.KnownFolders.Documents;
308 break;
309 case SpecialFolder.ProgramFiles:
310 folderGuid = Interop.Shell32.KnownFolders.ProgramFiles;
311 break;
312 case SpecialFolder.CommonProgramFiles:
313 folderGuid = Interop.Shell32.KnownFolders.ProgramFilesCommon;
314 break;
315 case SpecialFolder.AdminTools:
316 folderGuid = Interop.Shell32.KnownFolders.AdminTools;
317 break;
318 case SpecialFolder.CDBurning:
319 folderGuid = Interop.Shell32.KnownFolders.CDBurning;
320 break;
321 case SpecialFolder.CommonAdminTools:
322 folderGuid = Interop.Shell32.KnownFolders.CommonAdminTools;
323 break;
324 case SpecialFolder.CommonDocuments:
325 folderGuid = Interop.Shell32.KnownFolders.PublicDocuments;
326 break;
327 case SpecialFolder.CommonMusic:
328 folderGuid = Interop.Shell32.KnownFolders.PublicMusic;
329 break;
330 case SpecialFolder.CommonOemLinks:
331 folderGuid = Interop.Shell32.KnownFolders.CommonOEMLinks;
332 break;
333 case SpecialFolder.CommonPictures:
334 folderGuid = Interop.Shell32.KnownFolders.PublicPictures;
335 break;
336 case SpecialFolder.CommonStartMenu:
337 folderGuid = Interop.Shell32.KnownFolders.CommonStartMenu;
338 break;
339 case SpecialFolder.CommonPrograms:
340 folderGuid = Interop.Shell32.KnownFolders.CommonPrograms;
341 break;
342 case SpecialFolder.CommonStartup:
343 folderGuid = Interop.Shell32.KnownFolders.CommonStartup;
344 break;
345 case SpecialFolder.CommonDesktopDirectory:
346 folderGuid = Interop.Shell32.KnownFolders.PublicDesktop;
347 break;
348 case SpecialFolder.CommonTemplates:
349 folderGuid = Interop.Shell32.KnownFolders.CommonTemplates;
350 break;
351 case SpecialFolder.CommonVideos:
352 folderGuid = Interop.Shell32.KnownFolders.PublicVideos;
353 break;
354 case SpecialFolder.Fonts:
355 folderGuid = Interop.Shell32.KnownFolders.Fonts;
356 break;
357 case SpecialFolder.NetworkShortcuts:
358 folderGuid = Interop.Shell32.KnownFolders.NetHood;
359 break;
360 case SpecialFolder.PrinterShortcuts:
361 folderGuid = Interop.Shell32.KnownFolders.PrintersFolder;
362 break;
363 case SpecialFolder.UserProfile:
364 folderGuid = Interop.Shell32.KnownFolders.Profile;
365 break;
366 case SpecialFolder.CommonProgramFilesX86:
367 folderGuid = Interop.Shell32.KnownFolders.ProgramFilesCommonX86;
368 break;
369 case SpecialFolder.ProgramFilesX86:
370 folderGuid = Interop.Shell32.KnownFolders.ProgramFilesX86;
371 break;
372 case SpecialFolder.Resources:
373 folderGuid = Interop.Shell32.KnownFolders.ResourceDir;
374 break;
375 case SpecialFolder.LocalizedResources:
376 folderGuid = Interop.Shell32.KnownFolders.LocalizedResourcesDir;
377 break;
378 case SpecialFolder.SystemX86:
379 folderGuid = Interop.Shell32.KnownFolders.SystemX86;
380 break;
381 case SpecialFolder.Windows:
382 folderGuid = Interop.Shell32.KnownFolders.Windows;
383 break;
384 default:
385 return string.Empty;
388 return GetKnownFolderPath(folderGuid, option);
391 private static string GetKnownFolderPath(string folderGuid, SpecialFolderOption option)
393 Guid folderId = new Guid(folderGuid);
395 int hr = Interop.Shell32.SHGetKnownFolderPath(folderId, (uint)option, IntPtr.Zero, out string path);
396 if (hr != 0) // Not S_OK
398 return string.Empty;
401 return path;
404 #if FEATURE_APPX
405 private static class WinRTFolderPaths
407 private static Func<SpecialFolder, SpecialFolderOption, string>? s_winRTFolderPathsGetFolderPath;
409 public static string GetFolderPath(SpecialFolder folder, SpecialFolderOption option)
411 if (s_winRTFolderPathsGetFolderPath == null)
413 Type? winRtFolderPathsType = Type.GetType("System.WinRTFolderPaths, System.Runtime.WindowsRuntime, Version=4.0.14.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", throwOnError: false);
414 MethodInfo? getFolderPathsMethod = winRtFolderPathsType?.GetMethod("GetFolderPath", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static, null, new Type[] { typeof(SpecialFolder), typeof(SpecialFolderOption) }, null);
415 var d = (Func<SpecialFolder, SpecialFolderOption, string>?)getFolderPathsMethod?.CreateDelegate(typeof(Func<SpecialFolder, SpecialFolderOption, string>));
416 s_winRTFolderPathsGetFolderPath = d ?? delegate { return string.Empty; };
419 return s_winRTFolderPathsGetFolderPath(folder, option);
422 #endif
424 // Seperate type so a .cctor is not created for Enviroment which then would be triggered during startup
425 private static class WindowsVersion
427 // Cache the value in static readonly that can be optimized out by the JIT
428 internal static readonly bool IsWindows8OrAbove = GetIsWindows8OrAbove();
430 private static bool GetIsWindows8OrAbove()
432 ulong conditionMask = Interop.Kernel32.VerSetConditionMask(0, Interop.Kernel32.VER_MAJORVERSION, Interop.Kernel32.VER_GREATER_EQUAL);
433 conditionMask = Interop.Kernel32.VerSetConditionMask(conditionMask, Interop.Kernel32.VER_MINORVERSION, Interop.Kernel32.VER_GREATER_EQUAL);
434 conditionMask = Interop.Kernel32.VerSetConditionMask(conditionMask, Interop.Kernel32.VER_SERVICEPACKMAJOR, Interop.Kernel32.VER_GREATER_EQUAL);
435 conditionMask = Interop.Kernel32.VerSetConditionMask(conditionMask, Interop.Kernel32.VER_SERVICEPACKMINOR, Interop.Kernel32.VER_GREATER_EQUAL);
437 // Windows 8 version is 6.2
438 Interop.Kernel32.OSVERSIONINFOEX version = default;
439 unsafe
441 version.dwOSVersionInfoSize = sizeof(Interop.Kernel32.OSVERSIONINFOEX);
443 version.dwMajorVersion = 6;
444 version.dwMinorVersion = 2;
445 version.wServicePackMajor = 0;
446 version.wServicePackMinor = 0;
448 return Interop.Kernel32.VerifyVersionInfoW(ref version,
449 Interop.Kernel32.VER_MAJORVERSION | Interop.Kernel32.VER_MINORVERSION | Interop.Kernel32.VER_SERVICEPACKMAJOR | Interop.Kernel32.VER_SERVICEPACKMINOR,
450 conditionMask);