私は自分のプログラムの問題に取り組んでいたので、今日はあなたにとってラッキーな日です。解決策は簡単です。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
}