2017-02-22 1 views
1

J'écris un programme de sauvegarde qui a des erreurs. En parcourant le code avec un débogueur, je constate que j'obtiens des erreurs en supprimant des fichiers.DeleteFile() échoue mais le fichier est là (nom de fichier très long)

J'utilise CFileFind pour localiser les fichiers, et j'utilise CFileFind::GetFilePath() pour obtenir le chemin d'accès complet.

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() est de retour FALSE et GetLastError() est de retour 3 (ERROR_PATH_NOT_FOUND), et est dans les autres cas de retour 2 (ERROR_FILE_NOT_FOUND).

Comme vous pouvez le voir, je tente d'abord de supprimer l'attribut en lecture seule s'il est défini; cependant, je peux voir que le fichier existe et il n'a pas l'attribut en lecture seule.

Une chose à noter est que le nom du fichier est très long. Ce code a été testé et fonctionne plutôt bien avec des noms de fichiers plus courts. Dans ce cas, find.GetFilePath() retours:

\\ readyshare \ USB 3 \ Backups \ DRIVEZ_BACKUP \ Stacey \ Backup 0001 \ Music \ SUPPRESS \ iTunes \ iTunes Media \ Musique \ Dave Matthews Band \ Loin du monde (version Deluxe) \ Loin du monde (Deluxe Version.itlp \ audio \ DaveMatthewsBand_AwayFromTheWorld_backgroundaudio.m4a

Et cela semble correct. Si je copie tous, mais le nom de fichier dans l'Explorateur Windows, il me montre ce dossier. Et la le fichier existe dans ce dossier

Est-ce que quelqu'un sait pourquoi DeleteFile() me dirait que le chemin ou le fichier n'existe pas alors qu'en fait il le fait?

MISE À JOUR:

Sur la base de la réponse de Bruno Ferreira, je suis en cours d'exécution à travers mes noms de fichiers la méthode suivante. (Désolé pour l'ancien code de style CString, je suis mise à jour d'un programme MFC plus.)

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

Comme vous pouvez le voir le code, prepends le préfixe approprié si le nom du fichier dépasse MAX_PATH. Des mesures sont prises pour ajouter le préfixe approprié selon que le chemin spécifie ou non un chemin réseau.

Je ne sais pas pourquoi cela a été rendu incroyablement compliqué. Je ne vois vraiment pas de problème de compatibilité ascendante si Windows vous permet de spécifier un nom plus long. Sur Windows 10, il existe un paramètre de registre que vous pouvez modifier afin que cette absurdité ne soit pas requise. Mais bien sûr, je ne veux pas limiter mon logiciel aux versions de Windows ne peaufiné 10.

+1

Nice. Une downvote sans explication. C'est plutôt moche. Comment pourrais-je fournir plus de détails que j'ai ici? –

+0

'\ Readyshare \ ...' c'est exactement le chemin? ou peut être '\\ Readyshare \ ...' fichier pas local? – RbMm

+0

Le chemin commence avec deux barres obliques inverses, comme je l'ai dans ma question. C'est le chemin * exact *. C'est sur une clé USB qui est connectée à mon routeur. Et je n'ai eu aucun problème jusqu'à ce que je commence à obtenir ces noms de fichiers plus longs en raison de la structure du répertoire. –

Répondre

7

De MSDN:

Paramètres

lpFileName [en] Le nom du fichier à supprimé Dans la version ANSI de cette fonction, le nom est limité à MAX_PATH caractères. Pour étendre cette limite à 32 767 caractères larges, appelez la version Unicode de la fonction et ajoutez "\\? \" Au chemin. Pour plus d'informations, reportez-vous à la section Attribution d'un nom à un fichier.

Fondamentalement, vous devez appeler DeleteFileW préfixer \\?\ pour les chemins locaux et \\?\UNC\ pour les chemins éloignés, quelque chose comme ceci:

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

'\\? \' Préfixe sera travaillé uniquement pour le chemin absolu du système de fichiers local et ne fonctionnera pas pour le chemin réseau – RbMm

+2

@RbMm De MSDN: 'Le préfixe" \\? "Peut également être utilisé avec des chemins construits selon la convention de dénomination universelle (UNC). Pour spécifier un tel chemin en utilisant UNC, utilisez le préfixe "\\? \ UNC \". Par exemple, "\\? \ UNC \ server \ share", où "server" est le nom de l'ordinateur et "share" est le nom du dossier partagé.' https://msdn.microsoft.com/pt- br/bibliothèque/windows/bureau/aa365247 (v = vs.85) .aspx –

+0

c'est oui - '\\? \ UNC \' sera travail, mais dans votre code est '\\? \' ce qui ne va pas. vraiment quand Win32 sous-système vue '\\? \' préfixe il simplement le convertir en '\ ?? \' – RbMm