2009-11-18 63 views
7

非常に長い一時ファイルを最初に作成して閉じ、読み込み、読み込み、閉じてもう一度終了し、DeleteFile()を使って削除しようとする単一のスレッドプログラム(C++、Win32、NTFS)があります。DeleteFileは最近閉じたファイルで失敗します

通常はスムーズに進みますが、ときにはDeleteFile()が失敗し、GetLastError()がERROR_ACCESS_DENIEDを返します。ファイルは読み取り専用ではありません。どんなサイズのファイルでも起こりますが、その確率はファイルサイズとともに大きくなります。

何がファイルをロックしている可能性がありますか?私はWinInternalsのツールを使ってチェックし、疑わしいものは何も見つかりませんでした。

+0

あなたはそれを削除しようとする前に、適切にファイルを閉じていますか?あなたはどんなハンドルも見逃しましたか? – RageZ

+0

私が言ったように、私はWinInternalsツールでそれをチェックしました。すべてのオープンはクローズとペアになっていますが、削除は失敗します。 1秒間スリープ状態にすると問題が解決します。 –

+0

窓がバギーかもしれませんが、私はそれについて疑問に思っています。 'sleep'を追加するとうまくいくはずです^^ – RageZ

答えて

1

変更がまだキャッシュされていて、まだ保存されていない可能性がありますか?

これを確認するには、ファイルハンドルにWaitForSingleObjectを追加します。

+3

ライトキャッシングはアプリケーションに透過的です。行動の変化を引き起こしてはいけません。 –

4

DeleteFile()を呼び出す前にMessageBox()呼び出しを追加します。表示されたら、sysinternalsツールのProcess Explorerを実行します。 ファイルに対するオープンハンドルを検索します。すべてのハンドルをファイルにクローズしていない可能性があります。

+1

これは私が始めたものです。ハンドルはありません。だから私はファイルへのすべてのアクセスを記録し、何も特別なものはありません。 –

+0

競合状態(おそらくミリ秒単位)のように聞こえるので、すべてをフリーズしない限り、この方法でバグを再現することはできません。 (しかし、確かに試すことは、可能性を絞り込むのに役立ちます。) –

8

ウイルス対策ソフトウェアがインストールされていますか?そうした場合、リアルタイム保護機能を無効にしようとしましたか?

+3

この回答は完全に正当なものです。 –

+0

ああ、私はそれについて知らない。 –

8

Windowsはこの問題で有名です。 sqliteは、削除操作を100ミリ秒ごとに最大数まで再試行して問題を処理します。

あなたがオープンハンドルを持っていないと確信したら、実装でこれを実行すると、ウイルス対策ソフトウェアのようなものがファイルを開くときに頭痛を軽減します。参考のため

、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

私は、これはWindows Internalsで覆われていると信じています。短所は、ファイルハンドルでCloseHandleを呼び出したとしても、カーネルには数ミリ秒かかる閉じた参照が残っている可能性があるということです。

ファイルを削除するより確実な方法は、最後のハンドルを開くときにFILE_FLAG_DELETE_ON_CLOSEフラグを使用することです。これは、読み取り/書き込みの間にファイルを閉じるのを避けることができればさらに効果的です。

#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

他のプロセスが同じファイルを開いた場合、これは失敗しますか? "FILE_SHARE_DELETE共有モードですべて開いていない限り、ファイルへのオープンハンドルが存在すると、呼び出しは失敗します。" –

+0

はい、私は書き込みと読み取りの間にファイルハンドルを閉じないように勧めました。書き込みアクセス権のないファイルハンドルが本当に必要な場合は、FILE_FLAG_DELETE_ON_CLOSEで最初のハンドルを作成し、ReOpenFileまたはDuplicateHandleを使用します。 –

+0

おそらく私は今日は遅いですが、CreateFileの最後の呼び出しの前に誰かが卑劣なファイルを開くと、これはまだ問題にはなりませんか?最後のコールはまだ失敗します。 –

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; 
} 
関連する問題