2017-02-22 16 views
1

Я пишу программу резервного копирования с ошибками. Поступая через код с помощью отладчика, я обнаруживаю, что получаю ошибки при удалении файлов.DeleteFile() не работает, но файл есть (очень длинное имя файла)

Я использую 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() возвращается:

\\ ReadyShare \ USB 3 \ Подпорки \ DRIVEZ_BACKUP \ Stacey \ Backup 0001 \ Music \ TO УДАЛИТЬ \ ITunes \ ITunes Медиа \ Музыка \ Dave Matthews Band \ Вдали от мира (Deluxe Version) \ Вдали от World (Deluxe Version.itlp \ аудио \ DaveMatthewsBand_AwayFromTheWorld_backgroundaudio.m4a

И это выглядит правильно. Если я копирую все, но имя файла в Windows Explorer, он показывает мне эту папку. И файл существует в этой папке.

Кто-нибудь знает почему DeleteFile() сказал бы, что путь или файл не существует, когда на самом деле это так?

UPDATE:

На основании ответа Бруно Феррейра, я бег моих имен по следующему методу. (Извините за старый код 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.

+1

Nice. Сдвиг без объяснения. Это довольно бесхарактерно. Как я могу предоставить более подробную информацию, чем здесь? –

+0

'\ Readyshare \ ...' это точно путь? или может быть '\\ Readyshare \ ...' файл не локальным? – RbMm

+0

Путь начинается с двух обратных косых черт, как у меня в моем вопросе. Это * точный * путь. Это на USB-накопителе, подключенном к моему маршрутизатору. И у меня не было никаких проблем, пока я не начал получать эти более длинные имена файлов из-за структуры каталогов. –

ответ

7

От MSDN:

Параметры

lpFileName [в] Имя файла будет удален. В версии этой функции ANSI имя ограничено символами MAX_PATH. Чтобы расширить этот предел до 32 767 широких символов, вызовите версию функции Unicode и добавьте «\\? \» К пути. Для получения дополнительной информации о см. Именование файла.

В принципе, вы должны вызвать 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; 
    } 
} 
+1

'\\? \' Префикс будет работать только для абсолютного пути локальной файловой системы и не будет работать для сетевого пути – RbMm

+2

@RbMm Из MSDN: префикс \\? \ "Может также использоваться с путями, построенными в соответствии с универсальное соглашение об именах (UNC). Чтобы указать такой путь, используя UNC, используйте префикс «\\? \ UNC \». Например, «\\? \ UNC \ server \ share», где «server» - это имя компьютера, а «share» - это имя общей папки. «Https://msdn.microsoft.com/pt- br/library/windows/desktop/aa365247 (v = vs.85) .aspx –

+0

это да - '\\? \ UNC \' будет работать, но в вашем коде '\\? \' что не так. действительно, когда win32 подсистема видит '\\? \' префикс, он просто преобразует ее в '\ ?? \' – RbMm

Смежные вопросы