当第 4 个(可选)参数为 NULL 时,WriteFile 抛出访问冲突

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

认为它可能对某人有所帮助,因为它对我来说是一种惊喜。

WriteFile 函数尝试写入其第 4 个(可选)参数,如果它为 NULL,则会导致访问冲突异常...但不会在 Windows 8(.1).

这是来自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.

我创建了重现错误的简短示例。代码是在 Visual Studio 2008 SP1 中创建的 Win32 控制台应用程序:

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

最后,如果我通过以下两行更改对 WriteFile() 的调用,这将解决问题并适用于所有平台:

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

代码在 Windows 7 和 Windows XP SP3 上生成访问冲突(未在 Vista 上测试)。在 Windows 8(.1) 上它可以工作,即使我在第 4 个参数 (lpNumberOfBytesWritten) 中传递 NULL 也是如此。

实际问题是我开发了一个写入临时文件的模块,但是我忽略了第4个参数(我读了"optional"但误读了其余部分,认为它可能被忽略了)。我在 Windows 8.1 上开发并测试了它,所以代码运行良好,但客户端机器在 Windows 7 上,代码失败。

我的经验教训:我应该更用心(对细节),不要自鸣得意(并仔细测试)。

lpNumberOfBytesWritten 的文档说:

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

换句话说,lpNumberOfBytesWritten参数仅在使用重叠IO时才可选。您正在为 lpOverlapped 传递 NULL,因此不使用重叠 IO,lpNumberOfBytesWritten 不是可选的,不能是 NULL