2009-11-18 7 views
7

Ich habe ein single-threaded-Programm (C++, Win32, NTFS) das zuerst eine ziemlich lange temporäre Datei erstellt, schließt es, öffnet zum Lesen, liest, schließt wieder und versucht zu löschen mit DeleteFile().DeleteFile schlägt bei kürzlich geschlossener Datei fehl

Normalerweise geht es reibungslos, aber manchmal DeleteFile() fehlschlägt, und GetLastError() gibt ERROR_ACCESS_DENIED zurück. Die Datei ist sicher nicht schreibgeschützt. Es passiert bei Dateien beliebiger Größe, aber die Wahrscheinlichkeit wächst mit der Dateigröße.

Irgendwelche Ideen, was die Datei sperren könnte? Ich habe versucht, WinInternals Tools zu überprüfen und nichts Verdächtiges zu finden.

+0

Sind Sie sicher, dass Sie die Datei richtig, bevor Sie versuchen, sie löschen schließen? Hast du irgendwelche Griffe verpasst? – RageZ

+0

Wie ich schon sagte, habe ich das sogar mit WinInternals-Tools überprüft. Alle Öffnen werden mit Schließen gepaart, aber die Löschung schlägt fehl. Und das Hinzufügen von Schlaf für 1 Sekunde behebt das Problem. –

+0

Es könnte sein, dass Windows Buggy ist, aber ich bin etwas zweifelhaft. Wenn das Hinzufügen des "Schlafes" es funktionieren sollte, sollte es in Ordnung sein ^^ – RageZ

Antwort

1

Vielleicht sind die Änderungen noch zwischengespeichert und wurden noch nicht gespeichert?

Sie können dies überprüfen, indem Sie ein WaitForSingleObject auf dem Datei-Handle, um sicher zu sein.

+3

Schreibcache ist für die Anwendung transparent. Es sollte keine Änderung im Verhalten verursachen. –

4

Fügen Sie einen MessageBox() - Aufruf hinzu, bevor Sie DeleteFile() aufrufen. Wenn es angezeigt wird, führen Sie das sysinternals-Tool Process Explorer aus. Suchen Sie nach einem geöffneten Handle für die Datei. Wahrscheinlich haben Sie nicht alle Handles für die Datei geschlossen ...

+1

Das ist, was ich angefangen habe. Keine Griffe. Also habe ich dann alle Zugriffe auf die Datei protokolliert, und nichts besonderes. –

+0

Es klingt wie eine Race-Bedingung (möglicherweise in der Größenordnung von Millisekunden). Wenn Sie also nicht alles einfrieren, können Sie den Fehler möglicherweise nicht auf diese Weise reproduzieren. (Aber versuchen hilft sicherlich Möglichkeiten einzuschränken.) –

8

Nur eine wilde Vermutung - haben Sie eine Anti-Virus-Software installiert? Hast du versucht, irgendwelche Echtzeitschutzfunktionen darin zu deaktivieren, wenn du das tust?

+3

Diese Antwort verdient es, richtig zu sein. –

+0

Oh, darüber weiß ich nichts. –

8

Windows ist für dieses Problem berüchtigt. sqlite behandelt das Problem, indem es die Löschoperation alle 100 Millisekunden wiederholt, bis zu einer maximalen Anzahl.

Ich glaube, wenn Sie sicher sind, dass Sie keine offenen Handles haben, wird dies in Ihrer Implementierung sparen Sie einige Kopfschmerzen, wenn Dinge wie Antiviren-Software die Datei öffnen.

Als Referenz der Kommentar von SQLite Quelle:

/*                  
** 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

Ich glaube, das in Windows Internals bedeckt ist. Die kurze Geschichte ist, dass, obwohl Sie CloseHandle auf dem Datei-Handle aufgerufen haben, der Kernel noch ausstehende Verweise haben kann, die ein paar Millisekunden zum Schließen benötigen.

Eine zuverlässigere Möglichkeit, die Datei zu löschen, wenn Sie fertig sind, ist die Verwendung des FILE_FLAG_DELETE_ON_CLOSE-Flags beim Öffnen des letzten Handles. Dies funktioniert sogar noch besser, wenn Sie vermeiden können, die Datei zwischen Lese-/Schreibvorgängen zu schließen.

#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

Schlägt dies nicht fehl, wenn ein anderer Prozess die gleiche Datei geöffnet hat, wie in der folgenden Beschreibung der Dokumentation beschrieben? Msgstr "" "Wenn es offene Handles für eine Datei gibt, schlägt der Aufruf fehl, es sei denn, sie wurden alle mit dem Freigabe - Modus FILE_SHARE_DELETE geöffnet." –

+0

Ja, deshalb habe ich empfohlen, den Datei-Handle nicht zwischen den Schreib- und Leseoperationen zu schließen. Erstellen Sie das erste Handle mit FILE_FLAG_DELETE_ON_CLOSE, und verwenden Sie dann ReOpenFile oder DuplicateHandle, wenn Sie wirklich ein Dateihandle benötigen, das keinen Schreibzugriff hat. –

+0

Vielleicht bin ich heute langsam, aber wird das nicht immer ein Problem sein, wenn jemand die Datei vor dem letzten Aufruf von CreateFile öffnet? Der letzte Anruf wird immer noch fehlschlagen. –

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