Пример #1
0
        // 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);
        }
Пример #2
0
        // 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());
        }
Пример #3
0
        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;
        }
Пример #4
0
        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());
        }
Пример #5
0
        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;
        }
Пример #6
0
        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;
        }
Пример #7
0
        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);