2009-11-18 31 views
7

J'ai un seul programme fileté (C++, Win32, NTFS) qui crée d'abord un fichier temporaire assez long, le ferme, ouvre pour lire, lit, se referme et essaie de supprimer en utilisant DeleteFile().DeleteFile échoue sur le fichier récemment fermé

Habituellement, ça se passe bien, mais parfois DeleteFile() échoue, et GetLastError() renvoie ERROR_ACCESS_DENIED. Le fichier n'est pas seulement en lecture seule. Cela arrive sur des fichiers de n'importe quelle taille, mais la probabilité augmente avec la taille du fichier.

Des idées sur ce qui peut verrouiller le fichier? J'ai essayé les outils WinInternals pour vérifier et trouvé rien de suspect.

+0

Êtes-vous sûr que vous fermez le fichier correctement avant d'essayer de le supprimer? Avez-vous manqué des poignées? – RageZ

+0

Comme je l'ai dit, j'ai même vérifié cela avec les outils WinInternals. Toutes les ouvertures sont associées à des fermetures, mais la suppression échoue. Et ajouter dormir pendant 1 seconde résout le problème. –

+0

Ce pourrait être des fenêtres buggy mais je suis un peu douteux à ce sujet. si l'ajout de 'sleep' devrait fonctionner correctement ^^ – RageZ

Répondre

1

Peut-être que les modifications sont toujours mises en cache et n'ont pas encore été enregistrées?

Vous pouvez vérifier cela en ajoutant un WaitForSingleObject sur la poignée de fichier pour être sûr.

+3

La mise en cache d'écriture est transparente pour l'application. Cela ne devrait pas provoquer de changement de comportement. –

4

Ajouter un appel MessageBox() avant d'appeler DeleteFile(), Lorsqu'il apparaît, exécutez l'outil sysinternals Process Explorer. Recherchez un handle ouvert dans le fichier. Selon toute vraisemblance, vous n'avez pas fermé toutes les poignées du fichier ...

+1

Voilà ce que j'avais commencé. Pas de poignées. Alors je me suis connecté tout l'accès au fichier, et rien de spécial. –

+0

Cela ressemble à une condition de concurrence (peut-être de l'ordre des millisecondes), donc à moins de tout geler, vous ne pourrez peut-être pas reproduire le bug de cette façon. (Mais en essayant certainement aide à affiner les possibilités.) –

8

Vous avez un logiciel anti-virus installé? Avez-vous essayé de désactiver les fonctions de protection en temps réel, si vous le faites?

+3

Cette réponse mérite totalement d'avoir raison. –

+0

Oh, je ne sais pas à ce sujet. –

8

Windows est connu pour ce problème. sqlite gère le problème en réessayant l'opération de suppression toutes les 100 millisecondes jusqu'à un nombre maximal.

Je crois que si vous êtes sûr que vous n'avez aucune poignée ouverte, cela dans votre implémentation vous évitera des maux de tête lorsque des logiciels antivirus ouvriront le fichier.

Pour référence, le commentaire de la source SQLite:

/*                  
** Delete the named file.            
**                  
** Note that windows does not allow a file to be deleted if some other 
** process has it open. Sometimes a virus scanner or indexing program 
** will open a journal file shortly after it is created in order to do 
** whatever it does. While this other process is holding the   
** file open, we will be unable to delete it. To work around this  
** problem, we delay 100 milliseconds and try to delete again. Up  
** to MX_DELETION_ATTEMPTs deletion attempts are run before giving  
** up and returning an error.           
*/ 
3

Je crois que cela est couvert dans Windows Internals. L'histoire courte est que même si vous avez appelé CloseHandle sur le handle de fichier, le noyau peut toujours avoir des références en suspens qui prennent quelques millisecondes à fermer.

Une façon plus fiable de supprimer le fichier lorsque vous avez terminé est d'utiliser l'indicateur FILE_FLAG_DELETE_ON_CLOSE lors de l'ouverture du dernier handle. Cela fonctionne encore mieux si vous pouvez éviter de fermer le fichier entre les lectures/écritures.

#include <windows.h> 
#include <stdio.h> 

int wmain(int argc, wchar_t** argv) 
{ 
    LPCWSTR fileName = L"c:\\temp\\test1234.bin"; 

    HANDLE h1 = CreateFileW(
     fileName, 
     GENERIC_WRITE, 
     // make sure the next call to CreateFile can succeed if this handle hasn't been closed yet 
     FILE_SHARE_READ | FILE_SHARE_DELETE, 
     NULL, 
     CREATE_ALWAYS, 
     FILE_FLAG_SEQUENTIAL_SCAN | FILE_ATTRIBUTE_TEMPORARY, 
     NULL); 
    if (h1 == INVALID_HANDLE_VALUE) 
    { 
     fprintf(stderr, "h1 failed: 0x%x\n", GetLastError()); 
     return GetLastError(); 
    } 

    HANDLE h2 = CreateFileW(
     fileName, 
     GENERIC_READ, 
     // FILE_SHARE_WRITE is required in case h1 with GENERIC_WRITE access is still open 
     FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, 
     NULL, 
     OPEN_EXISTING, 
     // tell the OS to delete the file as soon as it is closed, no DeleteFile call needed 
     FILE_FLAG_DELETE_ON_CLOSE | FILE_FLAG_SEQUENTIAL_SCAN | FILE_ATTRIBUTE_TEMPORARY, 
     NULL); 
    if (h2 == INVALID_HANDLE_VALUE) 
    { 
     fprintf(stderr, "h2 failed: 0x%x\n", GetLastError()); 
     return GetLastError(); 
    } 

    return 0; 
} 
+0

Cela n'échouera-t-il pas si un autre processus a ouvert le même fichier, compte tenu de la description suivante de la documentation? "S'il existe des handles ouverts existants dans un fichier, l'appel échoue sauf s'ils ont tous été ouverts avec le mode de partage FILE_SHARE_DELETE." –

+0

Oui, c'est pourquoi j'ai recommandé de ne pas fermer la poignée de fichier entre les écritures et les lectures. Créez le premier handle avec FILE_FLAG_DELETE_ON_CLOSE, puis utilisez ReOpenFile ou DuplicateHandle si vous avez vraiment besoin d'un handle de fichier qui n'a pas d'accès en écriture. –

+0

Peut-être que je suis lent aujourd'hui, mais ce ne sera pas toujours un problème si quelqu'un sournois ouvre le fichier avant le dernier appel à CreateFile? le dernier appel échouera toujours. –

0
#include <iostream> 
#include <windows.h> 

int main(int argc, const char * argv[]) 
{ 
    // Get a pointer to the file name/path 
    const char * pFileToDelete = "h:\\myfile.txt"; 
    bool RemoveDirectory("h:\\myfile.txt"); 

    // try deleting it using DeleteFile 
    if(DeleteFile(pFileToDelete)) 
    { 
     // succeeded 
     std::cout << "Deleted file" << std::endl; 
    } 
    else 
    { 
     // failed 
     std::cout << "Failed to delete the file" << std::endl; 
    } 
    std::cin.get(); 
    return 0; 
}