// Returns a unique temporary file name, and creates a 0-byte file by that // name on disk. public static string GetTempFileName() { Span <char> initialTempPathBuffer = stackalloc char[PathInternal.MaxShortPath]; ValueStringBuilder tempPathBuilder = new ValueStringBuilder(initialTempPathBuffer); GetTempPath(ref tempPathBuilder); Span <char> initialBuffer = stackalloc char[PathInternal.MaxShortPath]; var builder = new ValueStringBuilder(initialBuffer); uint result = Interop.Kernel32.GetTempFileNameW( ref tempPathBuilder.GetPinnableReference(), "tmp", 0, ref builder.GetPinnableReference()); tempPathBuilder.Dispose(); if (result == 0) { throw Win32Marshal.GetExceptionForLastWin32Error(); } builder.Length = builder.RawChars.IndexOf('\0'); string path = PathHelper.Normalize(ref builder); builder.Dispose(); return(path); }
// This is only used by RegistryKey on Windows. internal static string ExpandEnvironmentVariables(string name) { Debug.Assert(name != null); if (name.Length == 0) { return(name); } Span <char> initialBuffer = stackalloc char[128]; var builder = new ValueStringBuilder(initialBuffer); uint length; while ((length = Win32Native.ExpandEnvironmentStringsW(name, ref builder.GetPinnableReference(), (uint)builder.Capacity)) > builder.Capacity) { builder.EnsureCapacity((int)length); } if (length == 0) { Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error()); } // length includes the null terminator builder.Length = (int)length - 1; return(builder.ToString()); }
private static void GetTempPath(ref ValueStringBuilder builder) { uint result; while ((result = Interop.Kernel32.GetTempPathW(builder.Capacity, ref builder.GetPinnableReference())) > builder.Capacity) { // Reported size is greater than the buffer size. Increase the capacity. builder.EnsureCapacity(checked ((int)result)); } if (result == 0) { throw Win32Marshal.GetExceptionForLastWin32Error(); } builder.Length = (int)result; }
private static string ExpandEnvironmentVariablesCore(string name) { Span <char> initialBuffer = stackalloc char[128]; var builder = new ValueStringBuilder(initialBuffer); uint length; while ((length = Interop.Kernel32.ExpandEnvironmentStrings(name, ref builder.GetPinnableReference(), (uint)builder.Capacity)) > builder.Capacity) { builder.EnsureCapacity((int)length); } if (length == 0) { Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error()); } // length includes the null terminator builder.Length = (int)length - 1; return(builder.ToString()); }
private static void GetUserName(ref ValueStringBuilder builder) { uint size = 0; while (Interop.Secur32.GetUserNameExW(Interop.Secur32.NameSamCompatible, ref builder.GetPinnableReference(), ref size) == Interop.BOOLEAN.FALSE) { if (Marshal.GetLastWin32Error() == Interop.Errors.ERROR_MORE_DATA) { builder.EnsureCapacity(checked ((int)size)); } else { builder.Length = 0; return; } } builder.Length = (int)size; }
private static void GetFullPathName(ReadOnlySpan <char> path, ref ValueStringBuilder builder) { // If the string starts with an extended prefix we would need to remove it from the path before we call GetFullPathName as // it doesn't root extended paths correctly. We don't currently resolve extended paths, so we'll just assert here. Debug.Assert(PathInternal.IsPartiallyQualified(path) || !PathInternal.IsExtended(path)); uint result; while ((result = Interop.Kernel32.GetFullPathNameW(ref MemoryMarshal.GetReference(path), (uint)builder.Capacity, ref builder.GetPinnableReference(), IntPtr.Zero)) > builder.Capacity) { // Reported size is greater than the buffer size. Increase the capacity. builder.EnsureCapacity(checked ((int)result)); } if (result == 0) { // Failure, get the error and throw int errorCode = Marshal.GetLastWin32Error(); if (errorCode == 0) { errorCode = Interop.Errors.ERROR_BAD_PATHNAME; } throw Win32Marshal.GetExceptionForWin32Error(errorCode, path.ToString()); } builder.Length = (int)result; }
internal static string TryExpandShortFileName(ref ValueStringBuilder outputBuilder, string?originalPath) { // We guarantee we'll expand short names for paths that only partially exist. As such, we need to find the part of the path that actually does exist. To // avoid allocating a lot we'll create only one input array and modify the contents with embedded nulls. Debug.Assert(!PathInternal.IsPartiallyQualified(outputBuilder.AsSpan()), "should have resolved by now"); // We'll have one of a few cases by now (the normalized path will have already: // // 1. Dos path (C:\) // 2. Dos UNC (\\Server\Share) // 3. Dos device path (\\.\C:\, \\?\C:\) // // We want to put the extended syntax on the front if it doesn't already have it (for long path support and speed), which may mean switching from \\.\. // // Note that we will never get \??\ here as GetFullPathName() does not recognize \??\ and will return it as C:\??\ (or whatever the current drive is). int rootLength = PathInternal.GetRootLength(outputBuilder.AsSpan()); bool isDevice = PathInternal.IsDevice(outputBuilder.AsSpan()); // As this is a corner case we're not going to add a stackalloc here to keep the stack pressure down. var inputBuilder = new ValueStringBuilder(); bool isDosUnc = false; int rootDifference = 0; bool wasDotDevice = false; // Add the extended prefix before expanding to allow growth over MAX_PATH if (isDevice) { // We have one of the following (\\?\ or \\.\) inputBuilder.Append(outputBuilder.AsSpan()); if (outputBuilder[2] == '.') { wasDotDevice = true; inputBuilder[2] = '?'; } } else { isDosUnc = !PathInternal.IsDevice(outputBuilder.AsSpan()) && outputBuilder.Length > 1 && outputBuilder[0] == '\\' && outputBuilder[1] == '\\'; rootDifference = PrependDevicePathChars(ref outputBuilder, isDosUnc, ref inputBuilder); } rootLength += rootDifference; int inputLength = inputBuilder.Length; bool success = false; int foundIndex = inputBuilder.Length - 1; while (!success) { uint result = Interop.Kernel32.GetLongPathNameW( ref inputBuilder.GetPinnableReference(terminate: true), ref outputBuilder.GetPinnableReference(), (uint)outputBuilder.Capacity); // Replace any temporary null we added if (inputBuilder[foundIndex] == '\0') { inputBuilder[foundIndex] = '\\'; } if (result == 0) { // Look to see if we couldn't find the file int error = Marshal.GetLastWin32Error(); if (error != Interop.Errors.ERROR_FILE_NOT_FOUND && error != Interop.Errors.ERROR_PATH_NOT_FOUND) { // Some other failure, give up break; } // We couldn't find the path at the given index, start looking further back in the string. foundIndex--; for (; foundIndex > rootLength && inputBuilder[foundIndex] != '\\'; foundIndex--) { ; } if (foundIndex == rootLength) { // Can't trim the path back any further break; } else { // Temporarily set a null in the string to get Windows to look further up the path inputBuilder[foundIndex] = '\0'; } } else if (result > outputBuilder.Capacity) { // Not enough space. The result count for this API does not include the null terminator. outputBuilder.EnsureCapacity(checked ((int)result)); } else { // Found the path success = true; outputBuilder.Length = checked ((int)result); if (foundIndex < inputLength - 1) { // It was a partial find, put the non-existent part of the path back outputBuilder.Append(inputBuilder.AsSpan(foundIndex, inputBuilder.Length - foundIndex)); } } } // If we were able to expand the path, use it, otherwise use the original full path result ref ValueStringBuilder builderToUse = ref (success ? ref outputBuilder : ref inputBuilder);