Search code examples
c++winapivisual-studio-2008access-violationwritefile

WriteFile throws Access Violation when 4th (optional) parameter is NULL


Thought that it may be helpful to someone because it was a kind of surprise for me.

WriteFile function tries to write into its 4th (optional) parameter, and if it is NULL it causes Access Violation exception... But not on Windows 8(.1).

This is the function definition from msdn:

BOOL WINAPI WriteFile(
    _In_         HANDLE hFile,
    _In_         LPCVOID lpBuffer,
    _In_         DWORD nNumberOfBytesToWrite,
    _Out_opt_    LPDWORD lpNumberOfBytesWritten, // Optional
    _Inout_opt_  LPOVERLAPPED lpOverlapped
);

lpNumberOfBytesWritten [out, optional] ... This parameter can be NULL only when the lpOverlapped parameter is not NULL.

I created short example that reproduces the error. The code is Win32 console application created in Visual Studio 2008 SP1:

int _tmain(int argc, _TCHAR* argv[])
{
    HANDLE hFile = CreateFile(L"N:\\Test\\Test.tmp", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY, NULL);
    if (INVALID_HANDLE_VALUE == hFile)  {
        return -1;
    }
    unsigned int testValue = 32;
    //-----------------------------------------------
    // Next line generates AV exception on Windows 7,
    // On Windows 8 it works fine:
    //-----------------------------------------------
    WriteFile(hFile, &testValue, sizeof(unsigned int), NULL, NULL);
    CloseHandle(hFile);
    return 0;
}

And, finally, if I change the call to WriteFile() by the following two lines, this solves the problem and works on all platforms:

// Now it does not generate AV:
DWORD written = 0;
WriteFile(hFile, &testValue, sizeof(unsigned int), &written, NULL);

The code generates Access Violation on Windows 7 and on Windows XP SP3 (did not test it on Vista). On Windows 8(.1) it works, even when I pass NULL in 4th parameter (lpNumberOfBytesWritten).

The actual problem was that I developed a module that writes into a temporary file, but I ignored the 4th parameter (I read "optional" but misread the rest and thought that it may be ignored). I developed and tested it on Windows 8.1, so the code worked fine, but the client machine was on Windows 7 and code failed.

The lessons I learned: I should be more attentive (to details) and don't be smug (and test carefully).


Solution

  • The documentation for lpNumberOfBytesWritten says:

    This parameter can be NULL only when the lpOverlapped parameter is not NULL.

    In other words, the lpNumberOfBytesWritten parameter is only optional when you are using overlapped IO. You are passing NULL for lpOverlapped, and therefore not using overlapped IO, the lpNumberOfBytesWritten is not optional and cannot be NULL.