Domanda

I need to write and rewrite a file reliably in Windows (preferably atomically). I am currently creating the file by doing this:

  • Build the file contents in memory
  • Call file_write with FILE.EXT.tmp
  • Call file_move with FILE.EXT and FILE.EXT.tmp

This usually works; however sporadically file_move returns 5 which is Access is denied. I don't know why this is happening but I need to be able to deal with it.

Currently I am working around this by calling my functions in a loop with 5 iterations and a 100ms delay in between writes if the write fails. This seems to work but I am wondering if there is a better way.

typedef long WIN32_ERROR;

WIN32_ERROR file_move(std::string const &dest, std::string const &src)
{
    if (MoveFileEx( src.c_str(), dest.c_str(), MOVEFILE_REPLACE_EXISTING|MOVEFILE_WRITE_THROUGH ))
        return 0;

    long err = GetLastError();
    if (err != ERROR_CALL_NOT_IMPLEMENTED && err != ERROR_NOT_SAME_DEVICE)
        return err;

    if (!CopyFile(src.c_str(), dest.c_str(), 0))
        return GetLastError();

    DeleteFile(src.c_str());
    return 0;
}

WIN32_ERROR file_write( std::string const &dest, void const *data, size_t data_len )
{
    HANDLE h = CreateFile( dest.c_str(), GENERIC_WRITE,
        FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, CREATE_ALWAYS,
        FILE_ATTRIBUTE_NOT_CONTENT_INDEXED | FILE_FLAG_SEQUENTIAL_SCAN |
        FILE_FLAG_WRITE_THROUGH,
        NULL );

    if ( h == INVALID_HANDLE_VALUE )
        return GetLastError();

    unsigned long n_written;
    if ( !WriteFile(h, data, data_len, &n_written, NULL) )
        return CloseHandle(h), GetLastError();

    if ( n_written != data_len )
    {
        CloseHandle(h);
        throw std::runtime_error("Write length error on file " + dest);
    }

    FlushFileBuffers(h);
    CloseHandle(h);

    return 0;
}
È stato utile?

Soluzione 2

Currently I am working around this by calling my functions in a loop with 5 iterations and a 100ms delay in between writes if the write fails. This seems to work but I am wondering if there is a better way.

Your file_move function is a function that operates on existing filesystem artifacts, i.e. it uses filenames to access existing stuff on disk.

Such operations are inherently "brittle" on contemporary Windows, especially wrt. Anti Malware Software as the other answer mentions, but also because of more mundane things like the file explorer still having a handle open.

"wondering if there is a better way." - there is no better way than re-trying and using fallbacks.

The more automated the process is, the more sophisticated you need to get: Iterative Re-tries, auto-determination of the locking process to inform the user who actually is holding a file, etc. etc.

Altri suggerimenti

The usual culprit is a virus scanner. They keep files in use at unpredictable moments..

I would use a more gradual backoff - wait less than 100 ms on the first attempt but more on the 5th attempt. You might want to throw up a messagebox after half a second to tell the user you're busy.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top