DeleteFile() 失败但文件存在(文件名很长)
DeleteFile() failing but the file is there (very long file name)
我正在编写一个有错误的备份程序。使用调试器单步执行代码,我发现删除文件时出错。
我使用 CFileFind
来定位文件,我使用 CFileFind::GetFilePath()
来获取完整路径名。
CFileFind find;
BOOL bContinue = find.FindFile(AppendPath(lpszPath, _T("*")));
while (bContinue)
{
bContinue = find.FindNextFile();
if (!find.IsDirectory())
{
if (find.IsReadOnly())
ClearReadOnlyAttribute(find);
if (!::DeleteFile(find.GetFilePath()))
return false;
}
}
DeleteFile()
返回 FALSE
,GetLastError()
返回 3 (ERROR_PATH_NOT_FOUND
),在其他情况下返回 2 (ERROR_FILE_NOT_FOUND
)。
如您所见,我首先尝试删除已设置的只读属性;但是,我可以看到该文件存在并且它没有只读属性。
需要注意的一点是文件名很长。这段代码实际上已经过测试并且可以很好地处理较短的文件名。在这种情况下,find.GetFilePath()
returns:
\Readyshare\USB 3\Backups\DRIVEZ_BACKUP\Stacey\Backup 0001\Music\TO BE DELETED\iTunes\iTunes Media\Music\Dave Matthews Band\Away from the World (Deluxe Version)\Away from the World (Deluxe Version.itlp\audio\DaveMatthewsBand_AwayFromTheWorld_backgroundaudio.m4a
这看起来是正确的。如果我将文件名以外的所有内容复制到 Windows Explorer 中,它会显示该文件夹。该文件存在于该文件夹中。
有谁知道为什么 DeleteFile()
会告诉我路径或文件不存在,而实际上它存在?
更新:
根据 Bruno Ferreira 的回答,我通过以下方法 运行 我的文件名。 (对于旧的 CString 样式代码,我很抱歉,我正在更新一个旧的 MFC 程序。)
CString CBackupWorker::ConvertToExtendedLengthPath(LPCTSTR pszPath)
{
CString s(pszPath);
if (s.GetLength() >= MAX_PATH)
{
if (::isalpha(s[0]) && s[1] == ':')
{
s.Insert(0, _T("\\?\"));
}
else if (s[0] == '\' && s[1] == '\')
{
s.Delete(0, 2);
s.Insert(0, _T("\\\?\UNC\"));
}
}
return s;
}
如代码所示,如果文件名超过 MAX_PATH
,则会在前面加上适当的前缀。根据路径是否指定网络路径,采取步骤附加适当的前缀。
我不知道为什么要把它搞得这么乱。如果 Windows 允许您指定更长的名称,我真的看不到向后兼容性问题。在 Windows 10 上,有一个您可以更改的注册表设置,这样就不需要这些废话了。但是,当然,我不想将我的软件限制为仅 Windows 10.
的调整版本
来自MSDN:
Parameters
lpFileName [in] The name of the file to be deleted. In the ANSI
version of this function, the name is limited to MAX_PATH characters.
To extend this limit to 32,767 wide characters, call the Unicode
version of the function and prepend "\?\" to the path. For more
information, see Naming a File.
基本上,您应该为本地路径调用 DeleteFileW
前缀 \?\
,为远程路径调用 \?\UNC\
,如下所示:
CFileFind find;
BOOL bContinue = find.FindFile(AppendPath(lpszPath, _T("*")));
while (bContinue)
{
bContinue = find.FindNextFile();
if (!find.IsDirectory())
{
if (find.IsReadOnly())
ClearReadOnlyAttribute(find);
CString path = find.GetFilePath();
if (path.GetLength() >= MAX_PATH)
{
if (PathIsUNC(path)) {
path.TrimLeft(_T("\"));
path.Insert(0, _T("\\?\UNC\"));
}
else
path.Insert(0, _T("\\?\"));
}
if (!::DeleteFileW(path))
return false;
}
}
我正在编写一个有错误的备份程序。使用调试器单步执行代码,我发现删除文件时出错。
我使用 CFileFind
来定位文件,我使用 CFileFind::GetFilePath()
来获取完整路径名。
CFileFind find;
BOOL bContinue = find.FindFile(AppendPath(lpszPath, _T("*")));
while (bContinue)
{
bContinue = find.FindNextFile();
if (!find.IsDirectory())
{
if (find.IsReadOnly())
ClearReadOnlyAttribute(find);
if (!::DeleteFile(find.GetFilePath()))
return false;
}
}
DeleteFile()
返回 FALSE
,GetLastError()
返回 3 (ERROR_PATH_NOT_FOUND
),在其他情况下返回 2 (ERROR_FILE_NOT_FOUND
)。
如您所见,我首先尝试删除已设置的只读属性;但是,我可以看到该文件存在并且它没有只读属性。
需要注意的一点是文件名很长。这段代码实际上已经过测试并且可以很好地处理较短的文件名。在这种情况下,find.GetFilePath()
returns:
\Readyshare\USB 3\Backups\DRIVEZ_BACKUP\Stacey\Backup 0001\Music\TO BE DELETED\iTunes\iTunes Media\Music\Dave Matthews Band\Away from the World (Deluxe Version)\Away from the World (Deluxe Version.itlp\audio\DaveMatthewsBand_AwayFromTheWorld_backgroundaudio.m4a
这看起来是正确的。如果我将文件名以外的所有内容复制到 Windows Explorer 中,它会显示该文件夹。该文件存在于该文件夹中。
有谁知道为什么 DeleteFile()
会告诉我路径或文件不存在,而实际上它存在?
更新:
根据 Bruno Ferreira 的回答,我通过以下方法 运行 我的文件名。 (对于旧的 CString 样式代码,我很抱歉,我正在更新一个旧的 MFC 程序。)
CString CBackupWorker::ConvertToExtendedLengthPath(LPCTSTR pszPath)
{
CString s(pszPath);
if (s.GetLength() >= MAX_PATH)
{
if (::isalpha(s[0]) && s[1] == ':')
{
s.Insert(0, _T("\\?\"));
}
else if (s[0] == '\' && s[1] == '\')
{
s.Delete(0, 2);
s.Insert(0, _T("\\\?\UNC\"));
}
}
return s;
}
如代码所示,如果文件名超过 MAX_PATH
,则会在前面加上适当的前缀。根据路径是否指定网络路径,采取步骤附加适当的前缀。
我不知道为什么要把它搞得这么乱。如果 Windows 允许您指定更长的名称,我真的看不到向后兼容性问题。在 Windows 10 上,有一个您可以更改的注册表设置,这样就不需要这些废话了。但是,当然,我不想将我的软件限制为仅 Windows 10.
的调整版本来自MSDN:
Parameters
lpFileName [in] The name of the file to be deleted. In the ANSI version of this function, the name is limited to MAX_PATH characters. To extend this limit to 32,767 wide characters, call the Unicode version of the function and prepend "\?\" to the path. For more information, see Naming a File.
基本上,您应该为本地路径调用 DeleteFileW
前缀 \?\
,为远程路径调用 \?\UNC\
,如下所示:
CFileFind find;
BOOL bContinue = find.FindFile(AppendPath(lpszPath, _T("*")));
while (bContinue)
{
bContinue = find.FindNextFile();
if (!find.IsDirectory())
{
if (find.IsReadOnly())
ClearReadOnlyAttribute(find);
CString path = find.GetFilePath();
if (path.GetLength() >= MAX_PATH)
{
if (PathIsUNC(path)) {
path.TrimLeft(_T("\"));
path.Insert(0, _T("\\?\UNC\"));
}
else
path.Insert(0, _T("\\?\"));
}
if (!::DeleteFileW(path))
return false;
}
}