2009-11-18 16 views
7

Tengo un único programa de subproceso (C++, Win32, NTFS) que primero crea un archivo temporal bastante largo, lo cierra, abre para leer, lee, cierra de nuevo e intenta eliminar utilizando DeleteFile().DeleteFile falla en el archivo cerrado recientemente

Suele pasar sin problemas, pero a veces DeleteFile() falla, y GetLastError() devuelve ERROR_ACCESS_DENIED. El archivo no es de solo lectura seguro. Ocurre en archivos de cualquier tamaño, pero la probabilidad aumenta con el tamaño del archivo.

¿Alguna idea de lo que puede estar bloqueando el archivo? Intenté con las herramientas de WinInternals para verificar y no encontré nada sospechoso.

+0

¿Seguro que está cerrando el archivo correctamente antes de intentar eliminarla? ¿Te perdiste alguna manija? – RageZ

+0

Como dije, incluso lo comprobé con las herramientas de WinInternals. Todas las aperturas están emparejadas con closes, pero la eliminación falla. Y agregar dormir durante 1 segundo soluciona el problema. –

+0

Puede que las ventanas tengan errores, pero estoy un poco dudoso sobre eso. si agrega 'sleep', hágala funcionar debería estar bien ^^ – RageZ

Respuesta

1

¿Quizás los cambios todavía estén en caché y aún no se hayan guardado?

Puede verificar esto agregando un WaitForSingleObject en el controlador del archivo para estar seguro.

+3

El almacenamiento en caché de escritura es transparente para la aplicación. No debería causar un cambio en el comportamiento. –

4

Agregue una llamada a MessageBox() antes de invocar DeleteFile(), cuando aparezca, ejecute la herramienta sysinternals Process Explorer. Busque un identificador abierto para el archivo. Con toda probabilidad, no ha cerrado todos los identificadores del archivo ...

+1

Eso es de lo que había empezado. Sin manijas Entonces registré todo el acceso al archivo y nada especial. –

+0

Suena como una condición de carrera (posiblemente del orden de milisegundos), así que, a menos que congele todo, es posible que no pueda reproducir el error de esta manera. (Pero intentar sin duda ayuda a reducir las posibilidades.) –

8

Simplemente una conjetura: ¿tiene algún software antivirus instalado? ¿Has intentado desactivar las características de protección en tiempo real, si lo haces?

+3

Esta respuesta totalmente merece estar en lo cierto. –

+0

Oh, no sé de eso. –

8

Windows es notorio por este problema. sqlite maneja el problema volviendo a intentar la operación de eliminación cada 100 milisegundos hasta un número máximo.

Creo que si está seguro de que no tiene controladores abiertos, hacer esto en su implementación le ahorrará algunos dolores de cabeza cuando cosas como el software antivirus abren el archivo.

Como referencia, el comentario de fuente 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

Creo que esto está cubierto en Windows Internals. La historia corta es que, aunque haya llamado a CloseHandle en el manejador de archivos, el kernel puede tener referencias pendientes que tarden unos pocos milisegundos en cerrarse.

Una manera más confiable de eliminar el archivo cuando haya terminado es utilizar el indicador FILE_FLAG_DELETE_ON_CLOSE al abrir el último identificador. Esto funciona incluso mejor si puede evitar cerrar el archivo entre lecturas/escrituras.

#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

¿No fallará si otro proceso ha abierto el mismo archivo, dada la siguiente descripción de la documentación? "Si hay identificadores abiertos existentes para un archivo, la llamada falla a menos que todos se hayan abierto con el modo de compartir FILE_SHARE_DELETE". –

+0

Sí, es por eso que recomendé no cerrar el identificador de archivo entre las escrituras y las lecturas. Cree el primer identificador con FILE_FLAG_DELETE_ON_CLOSE y luego use ReOpenFile o DuplicateHandle si realmente necesita un identificador de archivo que no tenga acceso de escritura. –

+0

Quizás estoy lento hoy, pero ¿no será un problema si alguien abre el archivo antes de la última llamada a CreateFile? la última llamada aún fallará –

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