【问题标题】:Need clarification for converting paths into long Unicode paths or the ones starting with \\?\需要澄清将路径转换为长 Unicode 路径或以 \\?\ 开头的路径
【发布时间】:2013-09-06 00:27:07
【问题描述】:

首先,让我问一个修辞问题——微软,为什么让我们在路径前加上 \\?\ 以允许它们有 32,767 个字符长?为什么不直接使用它们并在 API 上扩展内部缓冲区的大小?对不起,我只是在发泄我的不满......

好的,现在我的实际问题是,如果我有路径,如何将其转换为接受 32,767 个字符长度的格式?请注意,我对该路径一无所知——它可以是相对路径、绝对本地路径、网络共享等。换句话说,它可以是 Microsoft 发明的多种路径格式中的任何一种。

起初,在开头添加\\?\ 似乎是一个简单的提议,对吧?那么,如果该路径已经转换为扩展格式怎么办?我试过reading this,从那个页面的大小和底部的cmets数量,你可以看到事情并不像看起来那么简单。

【问题讨论】:

  • 我赞扬您找到并阅读您链接的文章,并意识到必须考虑的真正形式的弹幕。对您的问题的唯一简单建议是“如何将其转换为接受 32,767 个字符长度的格式?”这是:小心。您自己已经指出,在前面加上一个“\\?”并称其为好,并不是那么简单,而且文章的碎片也没有做出这样的假设,当然。这不难,但很乏味。
  • @WhozCraig:此时我正在询问是否有系统 API 可以执行此操作。如果没有,正如我所怀疑的那样,我正在寻求合作来编写这样的函数。
  • 我特别喜欢文章中功能失调的矛盾。在某处,“32,767 个字符的最大路径是近似的,因为“\\?\”前缀可能会在运行时被系统扩展为更长的字符串,并且这种扩展适用于总长度。”。再后来,“因为它关闭了路径字符串的自动扩展,“\\?\”前缀......”喜欢它
  • @WhozCraig:是的,那篇文章提出的问题比它回答的要多。我希望使用PathStripToRoot API 来帮助解决我最初的问题,但对于不同的操作系统,它的工作方式似乎不同。例如,从 Vista 开始它似乎工作正常,但在 XP 上它似乎没有检测到网络共享前缀......如果我想出一个工作代码,我会在这里发布以供您查看。
  • @WhozCraig:你可能知道,矛盾的出现是因为文档讨论了两个不同的事情。 "\\?\" 前缀禁止在 win32 级别对路径进行任何早期操作,但内核始终将路径扩展为 NT 命名空间路径(例如,"C:" 扩展为类似于 "\Device\HarddiskVolume2 ”)。因此,最大可用路径长度取决于系统如何命名您正在访问的设备。

标签: c++ windows winapi unicode path


【解决方案1】:

好的。事实证明,这并不像听起来那么简单。绊脚石(除了模糊的文档和无数的路径格式)是某些 API 甚至不能像宣传的那样在所有版本的操作系统上工作。

无论如何,这就是我想出的,它似乎可以在 XP SP3、Vista、Windows 7 和 8 上运行。有点大,也是为 MFC 编写的,但这仅用于字符串管理。我没时间调整:

enum PATH_PREFIX_TYPE
{
    PPT_UNKNOWN,
    PPT_ABSOLUTE,           //Found absolute path that is none of the other types
    PPT_UNC,                //Found \\server\share\ prefix
    PPT_LONG_UNICODE,       //Found \\?\ prefix
    PPT_LONG_UNICODE_UNC,   //Found \\?\UNC\ prefix
};

CString MakeUnicodeLargePath(LPCTSTR pPath)
{
    //Convert path from 'pPath' into a larger Unicode path, that allows up to 32,767 character length
    //RETURN:
    //      = Resulting path
    CString strPath;

    if(pPath &&
        pPath[0] != 0)
    {
        //Determine the type of the existing prefix
        PATH_PREFIX_TYPE ppt;
        GetOffsetAfterPathRoot(pPath, &ppt);

        //Assume path to be without change
        strPath = pPath;

        switch(ppt)
        {
        case PPT_ABSOLUTE:
            {
                //First we need to check if its an absolute path relative to the root
                BOOL bOK2AddPrefix = TRUE;
                if(strPath.GetLength() >= 1 &&
                    (strPath[0] == L'\\' || strPath[0] == L'/'))
                {
                    bOK2AddPrefix = FALSE;

                    //Get current root path
                    TCHAR chDummy[1];
                    DWORD dwLnSz = GetCurrentDirectory(0, chDummy);
                    if(dwLnSz)
                    {
                        TCHAR* pBuff = new (std::nothrow) TCHAR[dwLnSz];
                        if(pBuff)
                        {
                            if(GetCurrentDirectory(dwLnSz, pBuff) == dwLnSz - 1)
                            {
                                int nIndFollowing = GetOffsetAfterPathRoot(pBuff);
                                if(nIndFollowing > 0)
                                {
                                    bOK2AddPrefix = TRUE;
                                    CString strRoot = pBuff;
                                    strPath = strRoot.Left(nIndFollowing) + strPath.Right(strPath.GetLength() - 1);
                                }
                            }

                            delete[] pBuff;
                            pBuff = NULL;
                        }
                    }
                }

                if(bOK2AddPrefix)
                {
                    //Add \\?\ prefix
                    strPath = L"\\\\?\\" + strPath;
                }
            }
            break;

        case PPT_UNC:
            {
                //First we need to remove the opening slashes for UNC share
                if(strPath.GetLength() >= 2 &&
                    (strPath[0] == L'\\' || strPath[0] == L'/') &&
                    (strPath[1] == L'\\' || strPath[1] == L'/')
                    )
                {
                    strPath = strPath.Right(strPath.GetLength() - 2);
                }

                //Add \\?\UNC\ prefix
                strPath = L"\\\\?\\UNC\\" + strPath;
            }
            break;
        }
    }

    return strPath;
}

LPCTSTR PathSkipRoot_CorrectedForMicrosoftStupidity(LPCTSTR pszPath)
{
    //Correction for PathSkipRoot API
    CString strPath = pszPath;

    //Replace all /'s with \'s because PathSkipRoot can't handle /'s
    strPath.Replace(L'/', L'\\');

    //Now call the API
    LPCTSTR pResBuff = PathSkipRoot(strPath.GetString());

    return pResBuff ? pszPath + (UINT)(pResBuff - strPath.GetString()) : NULL;
}

BOOL PathIsRelative_CorrectedForMicrosoftStupidity(LPCTSTR pszPath)
{
    //Correction for PathIsRelative API
    CString strPath = pszPath;

    //Replace all /'s with \'s because PathIsRelative can't handle /'s
    strPath.Replace(L'/', L'\\');

    //Now call the API
    return PathIsRelative(strPath);
}

int GetOffsetAfterPathRoot(LPCTSTR pPath, PATH_PREFIX_TYPE* pOutPrefixType)
{
    //Checks if 'pPath' begins with the drive, share, prefix, etc
    //EXAMPLES:
    //   Path                          Return:   Points at:                 PrefixType:
    //  Relative\Folder\File.txt        0         Relative\Folder\File.txt   PPT_UNKNOWN
    //  \RelativeToRoot\Folder          1         RelativeToRoot\Folder      PPT_ABSOLUTE
    //  C:\Windows\Folder               3         Windows\Folder             PPT_ABSOLUTE
    //  \\server\share\Desktop          15        Desktop                    PPT_UNC
    //  \\?\C:\Windows\Folder           7         Windows\Folder             PPT_LONG_UNICODE
    //  \\?\UNC\server\share\Desktop    21        Desktop                    PPT_LONG_UNICODE_UNC
    //RETURN:
    //      = Index in 'pPath' after the root, or
    //      = 0 if no root was found
    int nRetInd = 0;
    PATH_PREFIX_TYPE ppt = PPT_UNKNOWN;

    if(pPath &&
        pPath[0] != 0)
    {
        int nLen = lstrlen(pPath);

        //Determine version of Windows
        OSVERSIONINFO osi;
        osi.dwOSVersionInfoSize = sizeof(osi);
        BOOL bWinXPOnly = GetVersionEx(&osi) && osi.dwMajorVersion <= 5;

        //The PathSkipRoot() API doesn't work correctly on Windows XP
        if(!bWinXPOnly)
        {
            //Works since Vista and up, but still needs correction :)
            LPCTSTR pPath2 = PathSkipRoot_CorrectedForMicrosoftStupidity(pPath);
            if(pPath2 &&
                pPath2 >= pPath)
            {
                nRetInd = pPath2 - pPath;
            }
        }

        //Now determine the type of prefix
        int nIndCheckUNC = -1;

        if(nLen >= 8 &&
            (pPath[0] == L'\\' || pPath[0] == L'/') &&
            (pPath[1] == L'\\' || pPath[1] == L'/') &&
            pPath[2] == L'?' &&
            (pPath[3] == L'\\' || pPath[3] == L'/') &&
            (pPath[4] == L'U' || pPath[4] == L'u') &&
            (pPath[5] == L'N' || pPath[5] == L'n') &&
            (pPath[6] == L'C' || pPath[6] == L'c') &&
            (pPath[7] == L'\\' || pPath[7] == L'/')
            )
        {
            //Found \\?\UNC\ prefix
            ppt = PPT_LONG_UNICODE_UNC;

            if(bWinXPOnly)
            {
                //For older OS
                nRetInd += 8;
            }

            //Check for UNC share later
            nIndCheckUNC = 8;
        }
        else if(nLen >= 4 &&
            (pPath[0] == L'\\' || pPath[0] == L'/') &&
            (pPath[1] == L'\\' || pPath[1] == L'/') &&
            pPath[2] == L'?' &&
            (pPath[3] == L'\\' || pPath[3] == L'/')
            )
        {
            //Found \\?\ prefix
            ppt = PPT_LONG_UNICODE;

            if(bWinXPOnly)
            {
                //For older OS
                nRetInd += 4;
            }
        }
        else if(nLen >= 2 &&
            (pPath[0] == L'\\' || pPath[0] == L'/') &&
            (pPath[1] == L'\\' || pPath[1] == L'/')
            )
        {
            //Check for UNC share later
            nIndCheckUNC = 2;
        }

        if(nIndCheckUNC >= 0)
        {
            //Check for UNC, i.e. \\server\share\ part
            int i = nIndCheckUNC;
            for(int nSkipSlashes = 2; nSkipSlashes > 0; nSkipSlashes--)
            {
                for(; i < nLen; i++)
                {
                    TCHAR z = pPath[i];
                    if(z == L'\\' ||
                        z == L'/' ||
                        i + 1 >= nLen)
                    {
                        i++;
                        if(nSkipSlashes == 1)
                        {
                            if(ppt == PPT_UNKNOWN)
                                ppt = PPT_UNC;

                            if(bWinXPOnly)
                            {
                                //For older OS
                                nRetInd = i;
                            }
                        }

                        break;
                    }
                }
            }
        }

        if(bWinXPOnly)
        {
            //Only if we didn't determine any other type
            if(ppt == PPT_UNKNOWN)
            {
                if(!PathIsRelative_CorrectedForMicrosoftStupidity(pPath + nRetInd))
                {
                    ppt = PPT_ABSOLUTE;
                }
            }

            //For older OS only
            LPCTSTR pPath2 = PathSkipRoot_CorrectedForMicrosoftStupidity(pPath + nRetInd);
            if(pPath2 &&
                pPath2 >= pPath)
            {
                nRetInd = pPath2 - pPath;
            }

        }
        else
        {
            //Only if we didn't determine any other type
            if(ppt == PPT_UNKNOWN)
            {
                if(!PathIsRelative_CorrectedForMicrosoftStupidity(pPath))
                {
                    ppt = PPT_ABSOLUTE;
                }
            }
        }
    }

    if(pOutPrefixType)
        *pOutPrefixType = ppt;

    return nRetInd;
}

这是我的测试方法:

_tprintf(MakeUnicodeLargePath(L""));
_tprintf(L"\n");
_tprintf(MakeUnicodeLargePath(L"C:\\Ba*d\\P|a?t<h>\\Windows\\Folder"));
_tprintf(L"\n");
_tprintf(MakeUnicodeLargePath(L"Relative\\Folder\\File.txt"));
_tprintf(L"\n");
_tprintf(MakeUnicodeLargePath(L"C:\\Windows\\Folder"));
_tprintf(L"\n");
_tprintf(MakeUnicodeLargePath(L"\\\\?\\C:\\Windows\\Folder"));
_tprintf(L"\n");
_tprintf(MakeUnicodeLargePath(L"\\\\?\\UNC\\server\\share\\Desktop"));
_tprintf(L"\n");
_tprintf(MakeUnicodeLargePath(L"\\\\?\\unC\\server\\share\\Desktop\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path"));
_tprintf(L"\n");
_tprintf(MakeUnicodeLargePath(L"\\\\server\\share\\Desktop\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path"));
_tprintf(L"\n");
_tprintf(MakeUnicodeLargePath(L"C:\\Desktop\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path"));
_tprintf(L"\n");
_tprintf(MakeUnicodeLargePath(L"\\AbsoluteToRoot\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path\\Very Long path"));
_tprintf(L"\n");
_tprintf(MakeUnicodeLargePath(L"\\\\server\\share\\Desktop"));
_tprintf(L"\n");
_tprintf(MakeUnicodeLargePath(L"\\\\?\\UNC\\\\share\\folder\\Desktop"));
_tprintf(L"\n");
_tprintf(MakeUnicodeLargePath(L"\\\\server\\share"));
_tprintf(L"\n");

这是我得到的输出:

\\?\C:\Ba*d\P|a?t<h>\Windows\Folder
Relative\Folder\File.txt
\\?\C:\Windows\Folder
\\?\C:\Windows\Folder
\\?\UNC\server\share\Desktop
\\?\unC\server\share\Desktop\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path
\\?\UNC\server\share\Desktop\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path
\\?\C:\Desktop\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path
\\?\C:\AbsoluteToRoot\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path
\\?\UNC\server\share\Desktop
\\?\UNC\\share\folder\Desktop
\\?\UNC\server\share

【讨论】:

  • 如果您需要它在不同的 Windows 操作系统版本上工作,请注意 UNC 仅支持 Windows 7 和更高版本,Windows Server 版本上的 UNC 支持我不清除,但我怀疑在 Windows Server 2008 之前不支持 UNC。
【解决方案2】:

今天是你的幸运日,因为我只是在为自己的程序解决问题。解决方案很简单:PathCchCanonicalizeEx 函数将在需要时添加“\?\”符号。但是这个功能有一些缺点,因为它需要使用 Windows 8 作为目标平台,这不是一个交易。这就是我制作自己的函数的方式,它接受“异常”路径字符串作为输入并输出准备好超过 MAX_PATH 限制的规范化字符串。它使用 Win8 SDK 中的新定义:

// max # of characters we support using the "\\?\" syntax
// (0x7FFF + 1 for NULL terminator)
#define PATHCCH_MAX_CCH             0x8000

函数声明如下(不是它需要定义 CString 并且它使用 SAL 注释):

//Normalize path and prepend "\\?\" if it exceeds the MAX_PATH limit
_Success_(return != false) //If the path was normalized and it exist (if bValidate == true) the return value is true and the normalized path is assigned to the sPath param
bool NormalizePath(_Inout_ CString & sPath, //Set to the abnormal path (relative, UNC, absolute), it is also used to store the normal path
    _In_ const CString sCurrentDir, //Set to the currend directory that will be used to resolve relative paths (used for security as GetCurrentDirectory function is thread-unsafe)
    _Out_opt_ bool *bDir = NULL, //Set to a bool pointer that will be set to true if the path points to a directory
    _In_ bool bValidate = false //If set the path existence is checked and the bDir argument is used
    );

以及定义(没有很多 cmets):

// max # of characters we support using the "\\?\" syntax
// (0x7FFF + 1 for NULL terminator)
#define PATHCCH_MAX_CCH             0x8000

#define LONG_PATH_ID                L"\\\\?\\"
#define UNC_PREFIX                  "\\\\"
#define UNC_LONG_ID                 L"\\\\?\\UNC\\"
#define CUR_DIR_REL_PATH_ID         ".\\"

#define WILDCAR_CHAR_ASTER          '*'
#define WILDCAR_CHAR_QUEMARK        '?'

#define DIR_DOWN                    ".."
#define DIR_UP                      "."

//Verify that a path exists and set bDir to true if the path points to an directory
bool ValidatePath(_In_ const TCHAR *sPath, _Out_ bool & bDir)
{
    //If the path contains a wildcards search test only the parent directory

    const TCHAR * sLastElement = _tcsrchr(sPath, _T('\\')); //The last component in the path

    bool bFoundWildCard = false;

    if(sLastElement)
    {
        ++sLastElement;
        if(_tcsrchr(sLastElement, _T(WILDCAR_CHAR_ASTER)) || _tcsrchr(sLastElement, _T(WILDCAR_CHAR_QUEMARK))) //If wilcard characters are contained in the last path component
        {
            bFoundWildCard = true;
            --sLastElement;
            const_cast<TCHAR *>(sLastElement)[0] = _T('\0');
        }
    }

    DWORD dwPathAttr = GetFileAttributes(sPath);

    if(dwPathAttr == INVALID_FILE_ATTRIBUTES)
    {
        _com_error sErrorMsg(GetLastError());
        //CProgramError.Set(sErrorMsg.ErrorMessage());
        return false;
    }

    bDir = dwPathAttr & FILE_ATTRIBUTE_DIRECTORY;

    if(bFoundWildCard)
    {
        const_cast<TCHAR *>(sLastElement)[0] = _T('\\');
    }

    return true;
}

void RespondPathComponent(_In_ const TCHAR *pComponent, _Out_ std::vector<CString> & vPathComponents)
{
    const TCHAR *pComponentLimiterR = _tcschr(pComponent, _T('\\'));

    const TCHAR *pComponentLimiterL = _tcschr(pComponent, _T('/'));

    const TCHAR *pComponentLimiter = NULL;

    if(pComponentLimiterR && pComponentLimiterL)
        pComponentLimiter = (pComponentLimiterR > pComponentLimiterL ? pComponentLimiterL : pComponentLimiterR);
    else
        pComponentLimiter = (pComponentLimiterR ? pComponentLimiterR : pComponentLimiterL);

    if(pComponentLimiter)
    {
        size_t szComponent = pComponentLimiter - pComponent;
        if(szComponent)
        {
            CString sTemp;
            sTemp.SetString(pComponent, szComponent);
            vPathComponents.push_back(sTemp);
        }
        ++pComponentLimiter;
        RespondPathComponent(pComponentLimiter, vPathComponents);
    }
    else
    {
        size_t szLastComp = _tcslen(pComponent);
        if(szLastComp)
        {
            CString sTemp;
            sTemp.SetString(pComponent, szLastComp);
            vPathComponents.push_back(sTemp);
        }
    }
}

size_t FixUpPathComponents(_Out_ std::vector<CString> & vPathComponents, _In_ const TCHAR *pPathComponents)
{
    RespondPathComponent(pPathComponents, vPathComponents);

    size_t szNumComponents = vPathComponents.size();

    for(size_t i(0); i < szNumComponents; ++i) //Check path components for special meanings
    {
        if(vPathComponents[i] == _T(DIR_DOWN))
        {
            vPathComponents.erase(vPathComponents.begin() + i); //Remove the current component
            --szNumComponents;
            if(i > 0)
            {
                vPathComponents.erase(vPathComponents.begin() + i - 1);
                --szNumComponents;
            }
        }
        else if(vPathComponents[i] == _T(DIR_UP))
        {
            if( (i + 1) < szNumComponents )
            {
                vPathComponents.erase(vPathComponents.begin() + i + 1);
                --szNumComponents;
            }
            vPathComponents.erase(vPathComponents.begin() + i); //Remove the current component
            --szNumComponents;
        }
    }

    return szNumComponents;
}

//Note that sCurrentDir is appended to all relative paths (nomatter the drive letter) - it needs to be a full path, not ending with '\\'
bool ExpandAndFixUpPath(_Inout_ CString & sPath, _In_opt_ const CString sCurrentDir)
{
    const size_t InPathStrSz = sPath.GetLength();

    if(!InPathStrSz) //Invalid path
        return false;

    //sPath.LockBuffer(); //Lock character buffer

    const TCHAR *PathBuffer = sPath.GetString(); //Retrieve the buffer

    if(InPathStrSz > 1) //To suppose the path is full it needs to have at lease 2 characters
    {
        if(_tcsstr(PathBuffer, _T(UNC_PREFIX)) == PathBuffer) //If the path begin with UNC_PREFIX
        {
            std::vector<CString> vPathComponents;

            size_t nComponents;

            if((nComponents = FixUpPathComponents(vPathComponents, &PathBuffer[2])) < 2) //A UNC path needs at leas two elements
                return false;

            sPath = _T('\\');

            for(size_t i(0); i < nComponents; ++i)
            {
                sPath += _T('\\');
                sPath += vPathComponents[i];
            }

            return true;
        }
        else if(PathBuffer[1] == _T(':')) //If the path begin with a disk designator
        {
            std::vector<CString> vPathComponents;

            if(FixUpPathComponents(vPathComponents, &PathBuffer[2]))
            {
                if(PathBuffer[2] == _T('\\') || PathBuffer[2] == _T('/'))
                {
                    sPath.SetString(PathBuffer, 2);

                    for(size_t i(0); i < vPathComponents.size(); ++i)
                    {
                        sPath += _T('\\');
                        sPath += vPathComponents[i];
                    }
                }
                else
                {
                    sPath = sCurrentDir;

                    for(size_t i(0); i < vPathComponents.size(); ++i)
                    {
                        sPath += _T('\\');
                        sPath += vPathComponents[i];
                    }
                }
            }
            else
            {
                sPath.SetString(PathBuffer, 2);
                sPath += _T('\\');
            }
            return true;
        }
    }

    std::vector<CString> vPathComponents;

    const TCHAR *pComponentsBegin = (_tcsstr(PathBuffer, _T(CUR_DIR_REL_PATH_ID)) == PathBuffer ? &PathBuffer[((sizeof(_T(CUR_DIR_REL_PATH_ID)) / sizeof(TCHAR)) - 1)] : PathBuffer);

    FixUpPathComponents(vPathComponents, pComponentsBegin);

    sPath = sCurrentDir;

    for(size_t i(0); i < vPathComponents.size(); ++i)
    {
        sPath += _T('\\');
        sPath += vPathComponents[i];
    }

    return true;
}

bool NormalizePath(_Inout_ CString & sPath, _In_ const CString sCurrentDir, _Out_opt_ bool *bDir = NULL, _In_ bool bValidate = false)
{
    if(!ExpandAndFixUpPath(sPath, sCurrentDir)) //Extend the path to it's full form
        return false;

    size_t LongPathLen = sPath.GetLength();

    const TCHAR *pPathBuf = sPath.GetString();

#ifdef _UNICODE

    if(LongPathLen <= (MAX_PATH - 1)) //If the path is longer in the range of MAX_PATH return it directly
    {
        if(bValidate)
            if(!ValidatePath(pPathBuf, *bDir)) //Validate path before returning it
                return false;

        return true;
    }

    bool bIsUNCPath = _tcsstr(pPathBuf, _T(UNC_PREFIX)) == pPathBuf;

    if(!bIsUNCPath)
    {
        if(LongPathLen > (PATHCCH_MAX_CCH - 1 - ((sizeof(LONG_PATH_ID) / sizeof(WCHAR)) - 1))) //If have no space to store the prefix fail
        {
            //CProgramError.Set(_T("Path too long!"));
            return false;
        }


        CString sPathTmp = LONG_PATH_ID;

        sPathTmp += pPathBuf;

        if(bValidate)
            if(!ValidatePath(sPathTmp.GetString(), *bDir)) //Validate path before returning it
                return false;

        sPath = sPathTmp;

        return true;
    }
    else
    {
        if( LongPathLen > ( PATHCCH_MAX_CCH - 1 - ((sizeof(UNC_LONG_ID) / sizeof(WCHAR)) - 1) + ((sizeof(_T(UNC_PREFIX)) / sizeof(WCHAR)) - 1) ) ) //If have no space to store the prefix fail
        {
            //CProgramError.Set(_T("Path too long!"));
            return false;
        }


        CString sPathTmp = UNC_LONG_ID;

        sPathTmp += &pPathBuf[2];

        if(bValidate)
            if(!ValidatePath(sPathTmp.GetString(), *bDir)) //Validate path before returning it
                return false;

        sPath = sPathTmp;

        return true;
    }

#else

    if(bValidate)
        if(!ValidatePath(pPathBuf, *bDir)) //Validate path before returning it
            return false;

    return true;

#endif
}

【讨论】:

  • 谢谢。首先,以“直观”的方式命名新 API,PathCchCanonicalizeEx,Microsoft :) 其次,我希望你早点发布它。我最终制定了自己的解决方法(我在上面发布了。)我看到你自己解析路径,这让我有点担心,因为我的方法依赖于现有的 Microsoft API 来完成大部分解析.
  • 我更喜欢使用我自己的方法,因为我使用的 GetFullPathName 函数不是线程安全的。这当然不是最好的实现。我真的很高兴微软添加了一个新的“MAX_PATH”值,所以我们最终能够处理长路径。我也希望下一个 Windows Explorer 也能支持它们。
  • Shell 是MAX_PATH 限制的 原因,我永远不会想到在那里寻找解决方案。很棒的发现。
【解决方案3】:

这是一个从我的一些旧 .NET 代码转换为未经测试的 C++ 代码的示例。

#include <iostream>
#include <string>

// For PathIsRelative() & PathCombine()
#include "Shlwapi.h"
#pragma comment(lib, "Shlwapi.lib") 

// For _wgetcwd()
#include <direct.h>

std::wstring ConvertToWin32Path(std::wstring &filepath);
std::wstring MakePathAbsolute(std::wstring &filepath);

int main()
{
    std::wstring filepath;
    std::wcout << L"Enter absolute file path (ex. \"c:\\temp\\myfile.txt\"):";
    std::wcin >> filepath;
    std::wcout << L"Converted file path: " << ConvertToWin32Path(filepath);
    return 0;
}

// If file path is disk file path then prepend it with \\?\
// if file path is UNC prepend it with \\?\UNC\ and remove \\ prefix in unc path.
std::wstring ConvertToWin32Path(std::wstring &filepath)
{
    if (filepath.compare(0, 2, L"\\\\"))
    {
        // If the path is relative then we need to convert it to a absolute path
        if (PathIsRelative(filepath.c_str()))
            filepath = MakePathAbsolute(filepath);

        filepath = std::wstring(L"\\\\?\\") + filepath;
    }
    else 
    {
        // Is the path already converted, if so then return the same filepath
        if (filepath.compare(0, 3, L"\\\\?"))
            filepath = std::wstring(L"\\\\?\\UNC\\") + filepath.substr(2, filepath.length() - 2);
    }

    return filepath;    
}

// Converts a relative path to a absolute one by adding the path of the executable
std::wstring MakePathAbsolute(std::wstring &filepath)
{
    // Convert from a relative to an absolute file path
    const size_t maxCurrDirLen = 256;
    wchar_t currentDir[maxCurrDirLen];
    _wgetcwd(currentDir, maxCurrDirLen - 1);
    const size_t maxPathLen = 32767;
    wchar_t fullPath[maxPathLen];
    PathCombine(fullPath, currentDir, filepath.c_str());
    return std::wstring(fullPath);
}

更新:

在得出结论之前你有没有测试过我的代码

“感谢您的尝试,但很抱歉这不是那么简单。”

?我已将代码的输出与您的新帖子的输入路径一起添加;你能说明我的代码在哪里失败吗?据我所知,我的代码比您的(更复杂的代码)的输出更准确。我能看到的唯一问题是 UNC 共享路径应该是 \\share\folder\Desktop 而不是 \\?\UNC\\share\folder\Desktop 但这是一个简单的修复 - 但是你自己的代码再次有同样的错误......

\\?\c:\dev\UNC_path_test
\\?\C:\Ba*d\P|a?t<h>\Windows\Folder
\\?\c:\dev\UNC_path_test\Relative\Folder\File.txt
\\?\C:\Windows\Folder
\\?\C:\Windows\Folder
\\?\UNC\server\share\Desktop
\\?\unC\server\share\Desktop\Very Long path\Very Long path\Very Long path\Very L
ong path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long p
ath\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\V
ery Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very L
ong path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long p
ath\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\V
ery Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very L
ong path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long p
ath\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\V
ery Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very L
ong path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long p
ath\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\V
ery Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very L
ong path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long p
ath\Very Long path\Very Long path\Very Long path
\\?\UNC\server\share\Desktop\Very Long path\Very Long path\Very Long path\Very L
ong path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long p
ath\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\V
ery Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very L
ong path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long p
ath\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\V
ery Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very L
ong path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long p
ath\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\V
ery Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very L
ong path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long p
ath\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\V
ery Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very L
ong path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long p
ath\Very Long path\Very Long path\Very Long path
\\?\C:\Desktop\Very Long path\Very Long path\Very Long path\Very Long path\Very
Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long
path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\
Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very
Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long
path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\
Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very
Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long
path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\
Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very
Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long
path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\
Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very
Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long
path\Very Long path\Very Long path
\\?\\AbsoluteToRoot\Very Long path\Very Long path\Very Long path\Very Long path\
Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very
Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long
path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\
Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very
Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long
path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\
Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very
Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long
path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\
Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very
Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long
path\Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\
Very Long path\Very Long path\Very Long path\Very Long path\Very Long path\Very
Long path\Very Long path\Very Long path
\\?\UNC\server\share\Desktop
\\?\UNC\\share\folder\Desktop
\\?\UNC\server\share

【讨论】:

  • 感谢您的尝试,但很抱歉。这不是那么简单。在另一篇文章中查看我的示例。
  • @c00000fd 您能否指出我的上述解决方案不支持哪些路径?我查看了您的新帖子,但没有发现问题。
  • @Inge 它不适用于相对路径。相对路径被明确声明为一项要求。对于 Microsoft 决定合并的任何未来扩展,它也将失败。
  • @IInspectable 在我的示例中可以看到,相对路径 Relative\\Folder\\File.txt 被转换为 \\?\c:\dev\UNC_path_test\Relative\Folder\File.txt,其中 \\?\c:\dev\UNC_path_test 是我的代码运行的目录。我应该期待其他结果吗?
  • @Inge 您正在调用PathCombine 获取相对路径。此 API 仅限于 MAX_PATH 个字符,无论您的实际缓冲区大小如何。您必须改用 PathCchCombineEx,它仅适用于 Windows 8/Windows Server 2012。同样,PathIsRelative 仅限于 MAX_PATH 个字符。
猜你喜欢
  • 2010-10-27
  • 1970-01-01
  • 2019-10-01
  • 2010-10-13
  • 2014-01-28
  • 2017-08-23
  • 1970-01-01
  • 1970-01-01
  • 2013-04-02
相关资源
最近更新 更多