c++ - seguir - no me funcionan los hashtags en instagram 2018
C++/Win32: ¿cómo esperar a que se complete una eliminación pendiente? (11)
¿Por qué no cambia el nombre del archivo que se va a eliminar y luego lo elimina?
Use GetTempFileName()
para obtener un nombre único, luego use MoveFile()
para cambiar el nombre del archivo. A continuación, elimine el archivo renombrado. Si la eliminación real es asíncrona y puede entrar en conflicto con la creación del mismo archivo (como parecen indicar las pruebas), esto debería resolver el problema.
Editar: por supuesto, si su análisis es correcto y las operaciones de archivos ocurren de manera asincrónica, esto podría presentar el problema de que usted intenta eliminar el archivo antes de que se realice el cambio de nombre. Pero entonces siempre puedes seguir intentando eliminar en un hilo de fondo.
Edición n. ° 2: si Hans tiene razón (y me inclino a creer en su análisis), mudarse podría no ser de gran ayuda, ya que es posible que no pueda cambiar el nombre de un archivo abierto por otro proceso. (Pero puede que sí, no sé esto.) Si ese es realmente el caso, la única otra forma en que puedo pensar es "sigue intentándolo". Tendría que esperar unos pocos milisegundos y volver a intentarlo. Mantenga un tiempo de espera para darse por vencido cuando esto no ayude.
Resuelto
* Solución viable: @sbi
* Explicación de lo que realmente sucede: @Hans
* Explicación de por qué OpenFile no pasa por "DELETE PENDING": @Benjamin
El problema:
Nuestro software es en gran parte un motor de interpretación para un lenguaje de scripting propietario. Ese lenguaje de scripting tiene la capacidad de crear un archivo, procesarlo y luego eliminar el archivo. Todas estas son operaciones separadas, y no se mantienen manejadores de archivos abiertos entre estas operaciones. (es decir, durante la creación del archivo, se crea un identificador, se usa para escribir y luego se cierra. Durante la parte de procesamiento del archivo, un identificador de archivo separado abre el archivo, lee de él y se cierra en EOF y, finalmente, elimina usos :: DeleteFile que solo tiene uso de un nombre de archivo, no un manejador de archivo en absoluto).
Recientemente nos hemos dado cuenta de que una macro particular (script) falla a veces para poder crear el archivo en algún momento posterior aleatorio (es decir, tiene éxito durante las primeras cien iteraciones de "crear, procesar, eliminar", pero cuando se trata de volver a crearlo ciento por primera vez, Windows responde "Acceso denegado").
Al profundizar en el tema, escribí un programa muy simple que pasa por algo como esto:
while (true) {
HANDLE hFile = CreateFileA(pszFilename, FILE_ALL_ACCESS, FILE_SHARE_READ, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
return OpenFailed;
const DWORD dwWrite = strlen(pszFilename);
DWORD dwWritten;
if (!WriteFile(hFile, pszFilename, dwWrite, &dwWritten, NULL) || dwWritten != dwWrite)
return WriteFailed;
if (!CloseHandle(hFile))
return CloseFailed;
if (!DeleteFileA(pszFilename))
return DeleteFailed;
}
Como puede ver, esto es directo a la API de Win32, y bastante simple. Creo un archivo, escribo en él, cierro el identificador, lo elimino, lo enjuago, repito ...
Pero en algún punto de la línea, obtendré un error de Acceso denegado (5) durante la llamada a CreateFile (). En cuanto a ProcessMonitor de sysinternal, puedo ver que el problema subyacente es que hay una eliminación pendiente en el archivo mientras intento crearlo de nuevo.
Preguntas:
* ¿Hay alguna manera de esperar a que se complete la eliminación?
* ¿Hay alguna manera de detectar que un archivo está pendiente de eliminación?
Hemos probado la primera opción, simplemente con WaitForSingleObject () en el HFILE. Pero el HFILE siempre está cerrado antes de que se ejecute WaitForSingleObject, por lo que WaitForSingleObject siempre devuelve WAIT_FAILED. Claramente, intentar esperar el identificador cerrado no funciona.
Podría esperar una notificación de cambio para la carpeta en la que existe el archivo. Sin embargo, eso parece ser un problema extremadamente elevado para lo que es un problema solo ocasionalmente (a saber: en mis pruebas en mi Win7 x64 E6600 PC normalmente falla) en la iteración 12000+: en otras máquinas, puede suceder tan pronto como la iteración 7 o 15 o 56 o nunca).
No he podido discernir ningún argumento CreateFile () que permita explícitamente este éter. No importa qué argumentos tenga CreateFile, realmente no está bien abrir un archivo para acceder cuando el archivo está pendiente de eliminación. Y como puedo ver este comportamiento tanto en un cuadro de XP como en un cuadro x64 Win7, estoy bastante seguro de que este es el comportamiento básico de NTFS "como estaba previsto" por parte de Microsoft. Así que necesito una solución que permita al sistema operativo completar la eliminación antes de intentar proceder, preferiblemente sin vincular los ciclos de la CPU innecesariamente, y sin la sobrecarga extrema de mirar la carpeta en la que se encuentra este archivo (si es posible).
Gracias por tomarse el tiempo para leer esto y publicar una respuesta. Preguntas aclaratorias bienvenidas!
[1] Sí, este ciclo devuelve una falla al escribir o una falla al cerrar las filtraciones, pero dado que esta es una aplicación de prueba de consola simple, la aplicación se cierra y Windows garantiza que el SO cierra todos los identificadores cuando un proceso completa. Entonces no hay filtraciones aquí.
bool DeleteFileNowA(const char * pszFilename)
{
// determine the path in which to store the temp filename
char szPath[MAX_PATH];
strcpy(szPath, pszFilename);
PathRemoveFileSpecA(szPath);
// generate a guaranteed to be unique temporary filename to house the pending delete
char szTempName[MAX_PATH];
if (!GetTempFileNameA(szPath, ".xX", 0, szTempName))
return false;
// move the real file to the dummy filename
if (!MoveFileExA(pszFilename, szTempName, MOVEFILE_REPLACE_EXISTING))
return false;
// queue the deletion (the OS will delete it when all handles (ours or other processes) close)
if (!DeleteFileA(szTempName))
return false;
return true;
}
Como está creando un nuevo archivo, procesándolo y luego eliminándolo, parece que realmente no le importa cuál es el nombre del archivo. Si ese es realmente el caso, debería considerar siempre crear un archivo temporal . De esta forma, cada vez que pasa el proceso, no tiene que preocuparse de que el archivo aún no se elimine.
Creo que esto es solo por un diseño deficiente en el sistema de archivos. He visto el mismo problema cuando trabajé con puertos de comunicación, abriendo / cerrando.
Lamentablemente, creo que la solución más simple sería volver a intentar crear el archivo varias veces si obtiene INVALID_HANDLE_VALUE
. GetLastError()
también podría brindarle una mejor forma de detectar este INVALID_HANDLE_VALUE
particular.
Hubiera preferido la superposición de E / S, pero CloseHandle()
y DeleteFile()
no manejan operaciones superpuestas :(
De acuerdo con [1] podría usar NtDeleteFile
para evitar la naturaleza asincrónica de DeleteFile. También [1] proporciona algunos detalles sobre cómo funciona DeleteFile.
Lamentablemente, la documentación oficial de NtDeleteFile
[2] no menciona ningún detalle en particular sobre este tema.
[1] http://undocumented.ntinternals.net/index.html?page=UserMode%2FUndocumented%20Functions%2FNT%20Objects%2FFile%2FNtDeleteFile.html [2] https://msdn.microsoft.com/en-us/library/windows/hardware/ff566435(v=vs.85).aspx
De hecho, tuve el mismo problema al usar LoadLibrary ( ruta ). No pude eliminar el archivo en la ruta .
La solución fue "cerrar el identificador" o utilizar el método FreeLibrary ( ruta ).
NOTA: Lea los "Comentarios" en MSDN respecto a FreeLibrary ().
Existen otros procesos en Windows que requieren una parte de ese archivo. El indexador de búsqueda es un candidato obvio. O un escáner de virus. Abrirán el archivo para compartirlo por completo, incluido FILE_SHARE_DELETE, para que otros procesos no se vean muy afectados por la apertura del archivo.
Eso generalmente funciona bien, a menos que crees / escribas / borres a una velocidad alta. La eliminación tendrá éxito, pero el archivo no puede desaparecer del sistema de archivos hasta que se cierre el último identificador. El identificador que tiene, por ejemplo, el indexador de búsqueda. Cualquier programa que intente abrir ese archivo de eliminación pendiente será abofeteado por el error 5.
Por lo demás, este es un problema genérico en un sistema operativo multitarea, no se puede saber qué otro proceso puede desear interferir con sus archivos. Su patrón de uso parece inusual; revíselo primero. Una solución alternativa sería detectar el error, dormir y volver a intentarlo. O moviendo el archivo a la papelera de reciclaje con SHFileOperation ().
Puede que llegue tarde a la fiesta, pero en Vista / Win7 hay DeleteFileTransacted que borra el archivo usando transacciones que aseguran que se eliminan (vacia archivos búferes, etc.). Para la compatibilidad de XP, esta no es una opción.
Otra idea de cómo se puede hacer esto es abrir OpenFile con el indicador OF_CREATE que establece la longitud en cero si el archivo existe o lo crea si no lo hace y luego llamar a FlushFileBuffers en el identificador de archivo para esperar a que finalice esta operación (haciendo que el archivo sea de longitud cero). . Al finalizar, el archivo tiene el tamaño 0 y simplemente llama a DeleteFile.
Luego puede probar si el archivo existe o si tiene longitud cero para tratarlo de la misma manera.
Quizás este no sea su problema en particular, pero es posible, así que le sugiero que saque Process Monitor (sysinternals) y vea.
Tuve exactamente el mismo problema y descubrí que Comodo Internet Security estaba contribuyendo al problema. Anteriormente tenía una máquina de doble núcleo, pero cuando actualicé a un Intel i7, de repente mi software de trabajo (jam.exe por el software Perfore) ya no funcionaba porque tenía el mismo patrón (una eliminación y luego crear, pero no verificar). Después de depurar el problema, encontré que GetLastError () devolvía el acceso denegado, pero el supervisor del proceso revela una ''eliminación pendiente''. Aquí está el rastro:
10:39:10.1738151 AM jam.exe 5032 CreateFile C:/Users/Dad/AppData/Local/Temp/jam5032t1.bat SUCCESS Desired Access: Read Attributes, Delete, Disposition: Open, Options: Non-Directory File, Open Reparse Point, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened
10:39:10.1738581 AM jam.exe 5032 QueryAttributeTagFile C:/Users/Dad/AppData/Local/Temp/jam5032t1.bat SUCCESS Attributes: ANCI, ReparseTag: 0x0
10:39:10.1738830 AM jam.exe 5032 SetDispositionInformationFile C:/Users/Dad/AppData/Local/Temp/jam5032t1.bat SUCCESS Delete: True
10:39:10.1739216 AM jam.exe 5032 CloseFile C:/Users/Dad/AppData/Local/Temp/jam5032t1.bat SUCCESS
10:39:10.1739438 AM jam.exe 5032 IRP_MJ_CLOSE C:/Users/Dad/AppData/Local/Temp/jam5032t1.bat SUCCESS
10:39:10.1744837 AM jam.exe 5032 CreateFile C:/Users/Dad/AppData/Local/Temp/jam5032t1.bat DELETE PENDING Desired Access: Generic Write, Read Attributes, Disposition: OverwriteIf, Options: Synchronous IO Non-Alert, Non-Directory File, Attributes: N, ShareMode: Read, Write, AllocationSize: 0
10:39:10.1788811 AM jam.exe 5032 CreateFile C:/Users/Dad/AppData/Local/Temp/jam5032t1.bat DELETE PENDING Desired Access: Generic Write, Read Attributes, Disposition: OverwriteIf, Options: Synchronous IO Non-Alert, Non-Directory File, Attributes: N, ShareMode: Read, Write, AllocationSize: 0
10:39:10.1838276 AM jam.exe 5032 CreateFile C:/Users/Dad/AppData/Local/Temp/jam5032t1.bat DELETE PENDING Desired Access: Generic Write, Read Attributes, Disposition: OverwriteIf, Options: Synchronous IO Non-Alert, Non-Directory File, Attributes: N, ShareMode: Read, Write, AllocationSize: 0
10:39:10.1888407 AM jam.exe 5032 CreateFile C:/Users/Dad/AppData/Local/Temp/jam5032t1.bat DELETE PENDING Desired Access: Generic Write, Read Attributes, Disposition: OverwriteIf, Options: Synchronous IO Non-Alert, Non-Directory File, Attributes: N, ShareMode: Read, Write, AllocationSize: 0
10:39:10.1936323 AM System 4 FASTIO_ACQUIRE_FOR_SECTION_SYNCHRONIZATION C:/Users/Dad/AppData/Local/Temp/jam5032t1.bat SUCCESS SyncType: SyncTypeOther
10:39:10.1936531 AM System 4 FASTIO_RELEASE_FOR_SECTION_SYNCHRONIZATION C:/Users/Dad/AppData/Local/Temp/jam5032t1.bat SUCCESS
10:39:10.1936647 AM System 4 IRP_MJ_CLOSE C:/Users/Dad/AppData/Local/Temp/jam5032t1.bat SUCCESS
10:39:10.1939064 AM jam.exe 5032 CreateFile C:/Users/Dad/AppData/Local/Temp/jam5032t1.bat DELETE PENDING Desired Access: Generic Write, Read Attributes, Disposition: OverwriteIf, Options: Synchronous IO Non-Alert, Non-Directory File, Attributes: N, ShareMode: Read, Write, AllocationSize: 0
10:39:10.1945733 AM cmdagent.exe 1188 CloseFile C:/Users/Dad/AppData/Local/Temp/jam5032t1.bat SUCCESS
10:39:10.1946532 AM cmdagent.exe 1188 IRP_MJ_CLOSE C:/Users/Dad/AppData/Local/Temp/jam5032t1.bat SUCCESS
10:39:10.1947020 AM cmdagent.exe 1188 IRP_MJ_CLOSE C:/Users/Dad/AppData/Local/Temp/jam5032t1.bat SUCCESS
10:39:10.1948945 AM cfp.exe 1832 QueryOpen C:/Users/Dad/AppData/Local/Temp/jam5032t1.bat FAST IO DISALLOWED
10:39:10.1949781 AM cfp.exe 1832 CreateFile C:/Users/Dad/AppData/Local/Temp/jam5032t1.bat NAME NOT FOUND Desired Access: Read Attributes, Disposition: Open, Options: Open Reparse Point, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a
10:39:10.1989720 AM jam.exe 5032 CreateFile C:/Users/Dad/AppData/Local/Temp/jam5032t1.bat SUCCESS Desired Access: Generic Write, Read Attributes, Disposition: OverwriteIf, Options: Synchronous IO Non-Alert, Non-Directory File, Attributes: N, ShareMode: Read, Write, AllocationSize: 0, OpenResult: Created
Como puede ver, hay una solicitud de eliminación seguida de varios intentos para abrir el archivo nuevamente mediante jam.exe (es un fopen en un bucle). Puede ver que cmdagent.exe presumiblemente tenía el archivo abierto, ya que cierra su identificador y de repente, jam.exe ahora puede abrir el archivo.
Por supuesto, la solución sugerida para esperar e intentar nuevamente, funciona bien.
Si CreateFile devuelve INVALID_HANDLE_VALUE, debe determinar qué GetLastError devuelve en su situación particular (pendiente de eliminación) y regresar a CreateFile únicamente en función de ese código de error.
Editar
¿La bandera FILE_FLAG_DELETE_ON_CLOSE te compra algo?
Una sugerencia tonta, dado que falla con poca frecuencia, ¿qué tal simplemente esperar algunos milisegundos en caso de falla y volver a intentarlo? O bien, si la latencia es importante, cambie a otro nombre de archivo, dejando el archivo anterior para eliminarlo más tarde.
- ¿Hay alguna forma de detectar que un archivo está pendiente de eliminación?
Utilice la función FILE_STANDARD_INFO con la estructura FILE_STANDARD_INFO .
Pero la función no puede resolver su problema. la solución de @ sbi tampoco.