Search code examples
c++.netfile-iocreatefile

Createfile2, ReadFile, and WriteFile; why isn't ReadFile reading what I've written in WriteFile?


For testing and to make sure I've got everything down so that I can start scaling things up later, I'm trying to create a file handle, write a given buffer of bytes to that file handle, then read part of that file into a new testing buffer.

I have the code:

const size_t vSize = 0x10000;

std::vector<byte> buffer(vSize, 0);

for (int i = 0; i != vSize; ++i)
{
    buffer[i] = i & 0xff;
}

std::wstring path = ApplicationData::Current->LocalFolder->Path->Data();

std::wstring testFileName = path + std::wstring(L"\\TestVariablySized");

_CREATEFILE2_EXTENDED_PARAMETERS extend = { 0 };
extend.dwSize = sizeof(CREATEFILE2_EXTENDED_PARAMETERS);
extend.dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
extend.dwFileFlags = FILE_FLAG_NO_BUFFERING;
extend.dwSecurityQosFlags = SECURITY_ANONYMOUS;
extend.lpSecurityAttributes = nullptr;
extend.hTemplateFile = nullptr;

HANDLE hMappedFile = CreateFile2(
    testFileName.c_str(),
    GENERIC_READ | GENERIC_WRITE,
    0,
    OPEN_ALWAYS,
    &extend);

_OVERLAPPED positionalData;
positionalData.Offset = 0;
positionalData.OffsetHigh = 0;
positionalData.hEvent = 0;

WriteFile(
    hMappedFile,
    &buffer[0],
    vSize,
    NULL,
    positionalData);

std::vector<byte> testBuffer(128);

ReadFile(
    hMappedFile,
    (LPVOID)&testBuffer[0],
    128,
    NULL,
    &positionalData);

Unfortunately, when I set a breakpoint after and check to see what's actually in testBuffer, I find that it's all zeroes. I've also tried all of the above without positionalData (i.e. replacing it with NULL in the calls to WriteFile/ReadFile), but that doesn't change the outcome. Similarly, I've tried this with NULL in place of the extended parameters for CreateFile2, same result.

I will eventually want to be able to choose an arbitrary location in a given file to read bytes from, so if I'm doing something weird with positionalData, please let me know.

As of right now, I don't know whether the problem is in CreateFile2, ReadFile, WriteFile, or some combination thereof. Your help is greatly appreciated!

edit: It turns out that ReadFile() is returning False, and the last error code after is 0x57 (87)--ERROR_INVALID_PARAMETER. Currently googling around on that, but in the event that I can't quite get the answer, I'm still interested in reading suggestions. It's not immediately obvious to me what I did wrong here, but my first guess is that I made a mistake with positionalData somehow.

final edit: removing FILE_FLAG_NO_BUFFERING did the job at the end. Thank you!


Solution

  • When you pass an OVERLAPPED structure to WriteFile()/ReadFile() when using a synchronous file handle, the functions will write/read bytes at the starting file offset specified by the OVERLAPPED, and then update the OVERLAPPED to contain the new file offset after the bytes that were written/read. You are passing the same OVERLAPPED to both WriteFile() and ReadFile(), but you are not rewinding the OVERLAPPED's offset before passing it to ReadFile(), so ReadFile() does not read the bytes that were previously written.

    ULARGE_INTEGER ulOffset;
    ...
    
    ulOffset.QuadPart = 0; // or whatever offset you need
    positionalData.Offset = ulOffset.LowPart;
    positionalData.OffsetHigh = ulOffset.HighPart;
    WriteFile(hMappedFile, ..., &positionalData);
    
    ...
    
    ulOffset.QuadPart = 0; // or whatever offset you need
    positionalData.Offset = ulOffset.LowPart;
    positionalData.OffsetHigh = ulOffset.HighPart;
    ReadFile(hMappedFile, ..., &positionalData);
    

    When you pass NULL instead of an OVERLAPPED structure, the functions will write/read bytes starting at the current file offset stored within the file handle itself, and then update the handle to store the new file offset after the bytes that were written/read. So, if you are using the same file handle for writing and reading, you will have to rewind the file handle's current offset using SetFilePointer() or SetFilePointerEx():

    LARGE_INTEGER liOffset;
    ...
    
    liOffset.QuadPart = 0; // or whatever offset you need
    SetFilePointerEx(hMappedFile, liOffset, NULL, FILE_BEGIN);
    WriteFile(hMappedFile, ..., NULL);
    
    ...
    
    liOffset.QuadPart = 0; // or whatever offset you need
    SetFilePointerEx(hMappedFile, liOffset, NULL, FILE_BEGIN);
    ReadFile(hMappedFile, ..., NULL);
    

    That being said, you are opening the file with the FILE_FLAG_NO_BUFFERING flag, which has very tight restrictions on what file offsets, buffer addresses, and buffer sizes can be during file I/O operations. Read the MSDN documentation for details about the specific rules you must follow when using that flag:

    File Buffering