11

まず最初に、修辞的な質問をさせてください -- Microsoft さん、パスを\\?\32,767 文字の長さにできるようにするために、なぜパスの前に を付けるのですか? それらをそのまま使用して、API の内部バッファーのサイズを拡張しないのはなぜですか? すみません、不満をぶつけてるだけです…

さて、実際の質問ですが、パスがある場合、それを 32,767 文字の長さを受け入れる形式に変換するにはどうすればよいですか? そのパスについては何も知らないことに注意してください-相対パス、絶対ローカルパス、ネットワーク共有などです。つまり、Microsoftが発明した多数のパス形式のいずれかです。

最初は、最初に追加するという単純な命題のように思え\\?\ますよね? では、そのパスが既に拡張形式に変換されている場合はどうでしょうか。これを読んでみたところ、そのページのサイズと下部のコメントの数から、物事が思ったほど単純ではないことがわかります。

4

3 に答える 3

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
    );

そして定義(多くのコメントなし):

// 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
}
于 2013-09-13T12:50:48.107 に答える