C++/Win32: Comment attendre une attente de suppression pour terminer?

Résolu:

* Solution: @sbi

* Explication de ce qui se passe réellement: @Hans

* Explication de pourquoi OpenFile ne passe pas à travers "en ATTENTE de SUPPRESSION": @Benjamin

Le Problème:

Notre logiciel est en grande partie un interprète moteur pour un propriétaire langage de script. Que langage de script a la possibilité de créer un fichier, un processus, puis supprimez le fichier. Ce sont toutes des opérations distinctes, et pas de descripteurs de fichiers sont maintenues ouvertes entre ces opérations. (c'est à dire pendant le fichier de créer un handle est créé, utilisés pour l'écriture, puis fermé. Pendant le traitement du fichier de la partie, un descripteur de fichier ouvre le fichier, lit, et qu'il est fermé à l'EOF. et Enfin, supprimer les usages ::DeleteFile qui a seulement l'utilisation d'un nom de fichier, pas un descripteur de fichier à tous).

Récemment, nous avons réalisé que l'un particulier de la macro (script) échoue parfois à être en mesure de créer le fichier au hasard moment ultérieur (c'est à dire qu'il réussit au cours de la première centaine d'itérations de "créer, de traiter, de les supprimer", mais quand il revient à la création d'une centaine de et des première fois, Windows réponses "Accès Refusé").

Chercher à approfondir la question, j'ai écrit un programme très simple qui passe en boucle sur quelque chose comme ceci:

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

Comme vous pouvez le voir, c'est direct à l'API Win32, et vachement simple. J'ai créer un fichier, écrire, fermez la poignée, le supprimer, rincer, répéter...

Mais quelque part le long de la ligne, je vais obtenir un Accès Refusé (5) erreur lors de la CreateFile() de l'appel. En regardant de sysinternals de ProcessMonitor, je peux voir que la question sous-jacente est qu'il y a une attente supprimer dans le fichier alors que je suis en train de le créer encore une fois.

Questions:

* Est-il possible d'attendre pour que la suppression complète?

* Est-il un moyen de détecter qu'un fichier est en attente de suppression?

Nous avons essayé la première option, il suffit de la WaitForSingleObject() sur le HFILE. Mais le HFILE est toujours fermé avant la WaitForSingleObject s'exécute, et ainsi de WaitForSingleObject retourne toujours WAIT_FAILED. Clairement, en essayant d'attendre la fermeture de la poignée ne fonctionne pas.

Je pouvais attendre une notification de changement pour le dossier que le fichier existe dans. Toutefois, cela semble être une très généraux-intensif quelque chose à ce qui est un problème, seulement de temps en temps (à savoir: dans mes tests sur mon Win7 x64 E6600 PC, il échoue généralement à l'itération 12000+ -- sur d'autres machines, il peut se produire dès que l'itération 7 ou 15 ou 56 ou jamais).

J'ai été incapable de discerner un CreateFile() arguments qui permettrait explicitement de cet éther. Peu importe ce que les arguments CreateFile a, c'est vraiment pas d'accord avec l'ouverture d'un fichier pour tout accès lorsque le fichier est en attente de suppression. Et depuis je ne peux voir ce comportement à la fois sur la boîte de XP et sur un Win7 x64 boîte, je suis tout à fait certain que c'est de base NTFS comportement "comme prévu" par Microsoft. J'ai donc besoin d'une solution qui permet à l'OS pour compléter le supprimer avant d'essayer de procéder, de préférence w/o lier des cycles CPU inutilement, et sans l'extrême surcharge de regarder le dossier que ce fichier est en cours (si possible).

Merci de prendre le temps de lire ceci et poste une réponse. Des Questions de clarification bienvenue!

[1] Oui, cette boucle de retour sur un échec d'écriture ou d'un échec de la fermeture qui fuit, mais puisque c'est une simple console application de test, l'application se termine, et Windows garantit que toutes les poignées sont fermés par le système d'exploitation lorsqu'un processus se termine. Donc pas de fuites existent pas ici.

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;
}
  • Vous assurer que toutes les poignées sont fermées? Parce que ce que vous avez écrit est exactement décrit sur le site MSDN: "Si vous appelez CreateFile sur un fichier qui est en attente de suppression suite à un précédent appel à DeleteFile, la fonction échoue. Le système d'exploitation retards, suppression de fichiers jusqu'à ce que toutes les poignées vers le fichier est fermé. GetLastError renvoie ERROR_ACCESS_DENIED."
  • J'ai créé une application console qui fait le code ci-dessus dans une boucle. Il échoue finalement... généralement autour de l'itération 12000-15000. Donc, sauf si vous voyez quelque façon que ci-dessus 8 lignes de code fuites d'une poignée, je pense que c'est tout à fait impossible (au moins pour ce test de l'applet).
  • Pour être plus humble à ce sujet: c'était notre hypothèse de départ ainsi. Mais après la chasse aux fantômes pendant des jours, je l'ai simplifié au-dessus, et comme je l'ai dit, les boucles qui présente le problème de manière cohérente (si au hasard).
  • merci - mais la dernière est fermée. C'est le problème: Windows est de le traiter comme s'il n'était pas fermé quand il est. J'ai besoin d'un moyen de synchroniser avec le système d'exploitation pour s'assurer que mon code attend pour la suppression complète.
  • Dans votre exemple de programme, si WriteFile échoue ou s'il était seulement une partie de l'écriture pour une raison quelconque, vous ne le pourrez pas fermer le handle de fichier. Alors peut-être vérifier il n'y a pas de court-circuit des rendements qui pourraient fuir le descripteur de fichier. À l'aide d'un garde de la classe qui ferme la poignée dans le dtor est l'approche la plus sûre.
  • Oui, dans un non-mannequin-exemple-code-clip, je garde à l'encontre de toutes les issues possibles à l'aide de RAII. Toutefois, pour que cette super-simple exemple, qui, tout simplement, quitte la boucle lors de toute situation d'échec se présente, je suis garanti que la poignée est fermé. Et la boucle n'est jamais essayer de continuer avec une fuite HFILE.
  • J'ai déjà vu cela et a mon commentaire supprimé.
  • Je me demande si il y a une sorte de sync() appel pour windows. Une recherche rapide a échoué à son tour, mais il peut être intéressant de regarder pour...
  • Intéressant et bien documenté question, +1. BTW, l'équivalent de la fonction de synchronisation de Windows doit être le FlushFileBuffers API.
  • Hans explication sonne comme il frappe le clou sur la tête. Il n'a pas l'air plausible pour moi que le système de fichiers lui-même pourrait être confus au sujet de son état interne, c'est à dire qu'il pourrait le croire en un fichier supprimé existait encore, seulement parce qu'il n'avait pas encore terminé l'ensemble de l'asynchrone de ménage associé à la suppression de ce fichier.
  • Ce qui est vraiment pose la question ici est, pourquoi ne pas l'OS de faire ce que je fais ci-dessus (permettre à un autre fichier qui sera créé par le même nom, tandis que l'ancien est toujours en attente de suppression). Il ne semble pas comme il n'y a aucune bonne raison de ne pas...
  • J'ai eu ce problème causé par le service Recherche Windows, il aurait remarqué, j'ai fait un répertoire et de le verrouiller à l'index, à la même époque, j'ai tenté de le supprimer et a reçu l'erreur 5. Il est facile de reproduire ce par la création et suppression de répertoires/fichiers dans une boucle avec l'indexeur activé.
  • Il a été MsMpEng dans mon cas, qui a bloqué le fichier. J'ai découvert MS confirmation que l'indice/antivirus SW peut bloquer le fichier. Maintenant, j'ai donc l'habitude de supprimer dans la boucle de nouvelle tentative ou mieux créer un nouveau fichier temp au lieu de réutiliser seul nom, avec l'espoir que la seule suppression sera effective en fin de compte. Maintenant, j'ai un problème similaire. Gcc p2 échoue dans gcc p.c > p.exe && p && gcc p2.c > p.exe && p ... parce que juste terminée p.exe est bloqué et rien dans filemon, mais p processus accède au fichier.

InformationsquelleAutor Mordachai | 2010-09-21