Example #1
0
        private static string TryExpandShortFileName(ref StringBuffer outputBuffer, 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 like crazy we'll create only one input array and modify the contents with embedded nulls.

            Debug.Assert(!PathInternal.IsPartiallyQualified(outputBuffer.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, 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(outputBuffer.AsSpan());
            bool isDevice   = PathInternal.IsDevice(outputBuffer.AsSpan());

            StringBuffer inputBuffer = new StringBuffer(0);

            try
            {
                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 \\.\)
                    inputBuffer.Append(ref outputBuffer);

                    if (outputBuffer[2] == '.')
                    {
                        wasDotDevice   = true;
                        inputBuffer[2] = '?';
                    }
                }
                else
                {
                    isDosUnc       = !PathInternal.IsDevice(outputBuffer.AsSpan()) && outputBuffer.Length > 1 && outputBuffer[0] == '\\' && outputBuffer[1] == '\\';
                    rootDifference = GetInputBuffer(ref outputBuffer, isDosUnc, ref inputBuffer);
                }

                rootLength += rootDifference;
                int inputLength = inputBuffer.Length;

                bool success    = false;
                int  foundIndex = inputBuffer.Length - 1;

                while (!success)
                {
                    uint result = Interop.Kernel32.GetLongPathNameW(inputBuffer.UnderlyingArray, outputBuffer.UnderlyingArray, (uint)outputBuffer.Capacity);

                    // Replace any temporary null we added
                    if (inputBuffer[foundIndex] == '\0')
                    {
                        inputBuffer[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 && inputBuffer[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
                            inputBuffer[foundIndex] = '\0';
                        }
                    }
                    else if (result > outputBuffer.Capacity)
                    {
                        // Not enough space. The result count for this API does not include the null terminator.
                        outputBuffer.EnsureCapacity(checked ((int)result));
                        result = Interop.Kernel32.GetLongPathNameW(inputBuffer.UnderlyingArray, outputBuffer.UnderlyingArray, (uint)outputBuffer.Capacity);
                    }
                    else
                    {
                        // Found the path
                        success             = true;
                        outputBuffer.Length = checked ((int)result);
                        if (foundIndex < inputLength - 1)
                        {
                            // It was a partial find, put the non-existent part of the path back
                            outputBuffer.Append(ref inputBuffer, foundIndex, inputBuffer.Length - foundIndex);
                        }
                    }
                }

                // Strip out the prefix and return the string
                ref StringBuffer bufferToUse = ref Choose(success, ref outputBuffer, ref inputBuffer);

                // Switch back from \\?\ to \\.\ if necessary
                if (wasDotDevice)
                {
                    bufferToUse[2] = '.';
                }

                string returnValue = null;

                int newLength = bufferToUse.Length - rootDifference;
                if (isDosUnc)
                {
                    // Need to go from \\?\UNC\ to \\?\UN\\
                    bufferToUse[PathInternal.UncExtendedPrefixLength - PathInternal.UncPrefixLength] = '\\';
                }

                // We now need to strip out any added characters at the front of the string
                if (bufferToUse.SubstringEquals(originalPath, rootDifference, newLength))
                {
                    // Use the original path to avoid allocating
                    returnValue = originalPath;
                }
                else
                {
                    returnValue = bufferToUse.Substring(rootDifference, newLength);
                }

                return(returnValue);
            }