Environment.Win32.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  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.
  4. using System.IO;
  5. using System.Text;
  6. using System.Runtime.InteropServices;
  7. namespace System
  8. {
  9. public static partial class Environment
  10. {
  11. public static string UserName
  12. {
  13. get
  14. {
  15. // 40 should be enough as we're asking for the SAM compatible name (DOMAIN\User).
  16. // The max length should be 15 (domain) + 1 (separator) + 20 (name) + null. If for
  17. // some reason it isn't, we'll grow the buffer.
  18. // https://support.microsoft.com/en-us/help/909264/naming-conventions-in-active-directory-for-computers-domains-sites-and
  19. // https://msdn.microsoft.com/en-us/library/ms679635.aspx
  20. Span<char> initialBuffer = stackalloc char[40];
  21. var builder = new ValueStringBuilder(initialBuffer);
  22. GetUserName(ref builder);
  23. ReadOnlySpan<char> name = builder.AsSpan();
  24. int index = name.IndexOf('\\');
  25. if (index != -1)
  26. {
  27. // In the form of DOMAIN\User, cut off DOMAIN\
  28. name = name.Slice(index + 1);
  29. }
  30. return name.ToString();
  31. }
  32. }
  33. private static void GetUserName(ref ValueStringBuilder builder)
  34. {
  35. uint size = 0;
  36. while (Interop.Secur32.GetUserNameExW(Interop.Secur32.NameSamCompatible, ref builder.GetPinnableReference(), ref size) == Interop.BOOLEAN.FALSE)
  37. {
  38. if (Marshal.GetLastWin32Error() == Interop.Errors.ERROR_MORE_DATA)
  39. {
  40. builder.EnsureCapacity(checked((int)size));
  41. }
  42. else
  43. {
  44. builder.Length = 0;
  45. return;
  46. }
  47. }
  48. builder.Length = (int)size;
  49. }
  50. public static string UserDomainName
  51. {
  52. get
  53. {
  54. // See the comment in UserName
  55. Span<char> initialBuffer = stackalloc char[40];
  56. var builder = new ValueStringBuilder(initialBuffer);
  57. GetUserName(ref builder);
  58. ReadOnlySpan<char> name = builder.AsSpan();
  59. int index = name.IndexOf('\\');
  60. if (index != -1)
  61. {
  62. // In the form of DOMAIN\User, cut off \User and return
  63. return name.Slice(0, index).ToString();
  64. }
  65. // In theory we should never get use out of LookupAccountNameW as the above API should
  66. // always return what we need. Can't find any clues in the historical sources, however.
  67. // Domain names aren't typically long.
  68. // https://support.microsoft.com/en-us/help/909264/naming-conventions-in-active-directory-for-computers-domains-sites-and
  69. Span<char> initialDomainNameBuffer = stackalloc char[64];
  70. var domainBuilder = new ValueStringBuilder(initialBuffer);
  71. uint length = (uint)domainBuilder.Capacity;
  72. // This API will fail to return the domain name without a buffer for the SID.
  73. // SIDs are never over 68 bytes long.
  74. Span<byte> sid = stackalloc byte[68];
  75. uint sidLength = 68;
  76. while (!Interop.Advapi32.LookupAccountNameW(null, ref builder.GetPinnableReference(), ref MemoryMarshal.GetReference(sid),
  77. ref sidLength, ref domainBuilder.GetPinnableReference(), ref length, out _))
  78. {
  79. int error = Marshal.GetLastWin32Error();
  80. // The docs don't call this out clearly, but experimenting shows that the error returned is the following.
  81. if (error != Interop.Errors.ERROR_INSUFFICIENT_BUFFER)
  82. {
  83. throw new InvalidOperationException(Win32Marshal.GetMessage(error));
  84. }
  85. domainBuilder.EnsureCapacity((int)length);
  86. }
  87. domainBuilder.Length = (int)length;
  88. return domainBuilder.ToString();
  89. }
  90. }
  91. private static string GetFolderPathCore(SpecialFolder folder, SpecialFolderOption option)
  92. {
  93. // We're using SHGetKnownFolderPath instead of SHGetFolderPath as SHGetFolderPath is
  94. // capped at MAX_PATH.
  95. //
  96. // Because we validate both of the input enums we shouldn't have to care about CSIDL and flag
  97. // definitions we haven't mapped. If we remove or loosen the checks we'd have to account
  98. // for mapping here (this includes tweaking as SHGetFolderPath would do).
  99. //
  100. // The only SpecialFolderOption defines we have are equivalent to KnownFolderFlags.
  101. string folderGuid;
  102. switch (folder)
  103. {
  104. case SpecialFolder.ApplicationData:
  105. folderGuid = Interop.Shell32.KnownFolders.RoamingAppData;
  106. break;
  107. case SpecialFolder.CommonApplicationData:
  108. folderGuid = Interop.Shell32.KnownFolders.ProgramData;
  109. break;
  110. case SpecialFolder.LocalApplicationData:
  111. folderGuid = Interop.Shell32.KnownFolders.LocalAppData;
  112. break;
  113. case SpecialFolder.Cookies:
  114. folderGuid = Interop.Shell32.KnownFolders.Cookies;
  115. break;
  116. case SpecialFolder.Desktop:
  117. folderGuid = Interop.Shell32.KnownFolders.Desktop;
  118. break;
  119. case SpecialFolder.Favorites:
  120. folderGuid = Interop.Shell32.KnownFolders.Favorites;
  121. break;
  122. case SpecialFolder.History:
  123. folderGuid = Interop.Shell32.KnownFolders.History;
  124. break;
  125. case SpecialFolder.InternetCache:
  126. folderGuid = Interop.Shell32.KnownFolders.InternetCache;
  127. break;
  128. case SpecialFolder.Programs:
  129. folderGuid = Interop.Shell32.KnownFolders.Programs;
  130. break;
  131. case SpecialFolder.MyComputer:
  132. folderGuid = Interop.Shell32.KnownFolders.ComputerFolder;
  133. break;
  134. case SpecialFolder.MyMusic:
  135. folderGuid = Interop.Shell32.KnownFolders.Music;
  136. break;
  137. case SpecialFolder.MyPictures:
  138. folderGuid = Interop.Shell32.KnownFolders.Pictures;
  139. break;
  140. case SpecialFolder.MyVideos:
  141. folderGuid = Interop.Shell32.KnownFolders.Videos;
  142. break;
  143. case SpecialFolder.Recent:
  144. folderGuid = Interop.Shell32.KnownFolders.Recent;
  145. break;
  146. case SpecialFolder.SendTo:
  147. folderGuid = Interop.Shell32.KnownFolders.SendTo;
  148. break;
  149. case SpecialFolder.StartMenu:
  150. folderGuid = Interop.Shell32.KnownFolders.StartMenu;
  151. break;
  152. case SpecialFolder.Startup:
  153. folderGuid = Interop.Shell32.KnownFolders.Startup;
  154. break;
  155. case SpecialFolder.System:
  156. folderGuid = Interop.Shell32.KnownFolders.System;
  157. break;
  158. case SpecialFolder.Templates:
  159. folderGuid = Interop.Shell32.KnownFolders.Templates;
  160. break;
  161. case SpecialFolder.DesktopDirectory:
  162. folderGuid = Interop.Shell32.KnownFolders.Desktop;
  163. break;
  164. case SpecialFolder.Personal:
  165. // Same as Personal
  166. // case SpecialFolder.MyDocuments:
  167. folderGuid = Interop.Shell32.KnownFolders.Documents;
  168. break;
  169. case SpecialFolder.ProgramFiles:
  170. folderGuid = Interop.Shell32.KnownFolders.ProgramFiles;
  171. break;
  172. case SpecialFolder.CommonProgramFiles:
  173. folderGuid = Interop.Shell32.KnownFolders.ProgramFilesCommon;
  174. break;
  175. case SpecialFolder.AdminTools:
  176. folderGuid = Interop.Shell32.KnownFolders.AdminTools;
  177. break;
  178. case SpecialFolder.CDBurning:
  179. folderGuid = Interop.Shell32.KnownFolders.CDBurning;
  180. break;
  181. case SpecialFolder.CommonAdminTools:
  182. folderGuid = Interop.Shell32.KnownFolders.CommonAdminTools;
  183. break;
  184. case SpecialFolder.CommonDocuments:
  185. folderGuid = Interop.Shell32.KnownFolders.PublicDocuments;
  186. break;
  187. case SpecialFolder.CommonMusic:
  188. folderGuid = Interop.Shell32.KnownFolders.PublicMusic;
  189. break;
  190. case SpecialFolder.CommonOemLinks:
  191. folderGuid = Interop.Shell32.KnownFolders.CommonOEMLinks;
  192. break;
  193. case SpecialFolder.CommonPictures:
  194. folderGuid = Interop.Shell32.KnownFolders.PublicPictures;
  195. break;
  196. case SpecialFolder.CommonStartMenu:
  197. folderGuid = Interop.Shell32.KnownFolders.CommonStartMenu;
  198. break;
  199. case SpecialFolder.CommonPrograms:
  200. folderGuid = Interop.Shell32.KnownFolders.CommonPrograms;
  201. break;
  202. case SpecialFolder.CommonStartup:
  203. folderGuid = Interop.Shell32.KnownFolders.CommonStartup;
  204. break;
  205. case SpecialFolder.CommonDesktopDirectory:
  206. folderGuid = Interop.Shell32.KnownFolders.PublicDesktop;
  207. break;
  208. case SpecialFolder.CommonTemplates:
  209. folderGuid = Interop.Shell32.KnownFolders.CommonTemplates;
  210. break;
  211. case SpecialFolder.CommonVideos:
  212. folderGuid = Interop.Shell32.KnownFolders.PublicVideos;
  213. break;
  214. case SpecialFolder.Fonts:
  215. folderGuid = Interop.Shell32.KnownFolders.Fonts;
  216. break;
  217. case SpecialFolder.NetworkShortcuts:
  218. folderGuid = Interop.Shell32.KnownFolders.NetHood;
  219. break;
  220. case SpecialFolder.PrinterShortcuts:
  221. folderGuid = Interop.Shell32.KnownFolders.PrintersFolder;
  222. break;
  223. case SpecialFolder.UserProfile:
  224. folderGuid = Interop.Shell32.KnownFolders.Profile;
  225. break;
  226. case SpecialFolder.CommonProgramFilesX86:
  227. folderGuid = Interop.Shell32.KnownFolders.ProgramFilesCommonX86;
  228. break;
  229. case SpecialFolder.ProgramFilesX86:
  230. folderGuid = Interop.Shell32.KnownFolders.ProgramFilesX86;
  231. break;
  232. case SpecialFolder.Resources:
  233. folderGuid = Interop.Shell32.KnownFolders.ResourceDir;
  234. break;
  235. case SpecialFolder.LocalizedResources:
  236. folderGuid = Interop.Shell32.KnownFolders.LocalizedResourcesDir;
  237. break;
  238. case SpecialFolder.SystemX86:
  239. folderGuid = Interop.Shell32.KnownFolders.SystemX86;
  240. break;
  241. case SpecialFolder.Windows:
  242. folderGuid = Interop.Shell32.KnownFolders.Windows;
  243. break;
  244. default:
  245. return string.Empty;
  246. }
  247. return GetKnownFolderPath(folderGuid, option);
  248. }
  249. private static string GetKnownFolderPath(string folderGuid, SpecialFolderOption option)
  250. {
  251. Guid folderId = new Guid(folderGuid);
  252. int hr = Interop.Shell32.SHGetKnownFolderPath(folderId, (uint)option, IntPtr.Zero, out string path);
  253. if (hr != 0) // Not S_OK
  254. {
  255. return string.Empty;
  256. }
  257. return path;
  258. }
  259. }
  260. }