C++ WriteFilex-CompletionRoutine:这是Windows7-64位中的一个bug吗?
似乎我最近在Windows7 ultimate 64位中发现了一个巨大的bug。 奇怪的是,我在谷歌或MSDN上找不到任何关于它的信息。 我似乎不可能是第一个发现API中的bug的人,API如此重要,就像市场上很长一段时间以来的操作系统中的C++ WriteFilex-CompletionRoutine:这是Windows7-64位中的一个bug吗?,c++,windows,winapi,64-bit,C++,Windows,Winapi,64 Bit,似乎我最近在Windows7 ultimate 64位中发现了一个巨大的bug。 奇怪的是,我在谷歌或MSDN上找不到任何关于它的信息。 我似乎不可能是第一个发现API中的bug的人,API如此重要,就像市场上很长一段时间以来的操作系统中的writeFilex 但我的代码太简单了,不会出错 除此之外,我的代码在以下方面工作得非常完美: Windows XP prof 32位, Windows Vista ultimate 32位, Windows 7 ultimate 32位, 如果编译为64位
writeFilex
但我的代码太简单了,不会出错
除此之外,我的代码在以下方面工作得非常完美:
Windows XP prof 32位,
Windows Vista ultimate 32位,
Windows 7 ultimate 32位,
如果编译为64位,它甚至可以在Windows7终极64位上运行
如果编译为32位,则唯一的故障发生在Windows 7-64位上
发生的情况是,文件被正确写入磁盘,完成例程被调用,但完成例程报告已写入零字节,最奇怪的是,重叠的
结构具有错误的地址和无效的内容
有人能确认这是一个bug吗
void WINAPI CompletionRoutine(DWORD u32_ErrorCode, DWORD u32_BytesTransfered,
OVERLAPPED* pk_Overlapped)
{
printf("CompletionRoutine: Transferred: %d Bytes, AddrOverlapped: 0x%X\n",
u32_BytesTransfered, (DWORD_PTR)pk_Overlapped);
}
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
printf("Compiled as: %d Bit\n", sizeof(DWORD_PTR) == 8 ? 64 : 32);
HANDLE h_File = CreateFileW(L"E:\\Temp\\Test.txt", GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_WRITE, 0,
CREATE_ALWAYS, 0, 0);
OVERLAPPED k_Over = {0};
printf("Before WriteFileEx AddrOverlapped: 0x%X\n", (DWORD_PTR)&k_Over);
WriteFileEx(h_File, "ABCDEF", 6, &k_Over, CompletionRoutine);
printf("Before SleepEx\n");
SleepEx(1000, TRUE);
printf("Exit\n");
return 0;
}
结果如下:
已解决:
缺少重叠的文件标志
我仍然认为,将一个完全损坏的指针传递给回调例程,回调告诉它已写入0字节,尽管它实际上已写入6字节,这肯定是一个严重的错误
正如我所展示的:并不是所有的操作系统都有这个bug
正确的行为是像Windows XP一样使用正确的重叠地址调用回调例程,或者如果重叠结构与文件句柄一起使用,而文件句柄未使用文件标志重叠,则返回错误\u无效\u参数
我发现的东西非常难看,因为如果你在XP、Vista、Win32位上开发和测试你的应用程序,你将永远不会发现缺少一个标志。然后你将你的应用程序交付给使用Win 7-64位的最终用户,他们会告诉你你的程序无法运行
如果给出错误的参数,正确编程的Windows API必须返回错误,但决不能传递损坏的指针
我测试的5个操作系统中有4个通过了正确的地址 在实际调用完成例程之前,您不会执行任何错误检查,也不会确保重叠的
结构保持活动状态(可能需要1秒以上)
请尝试类似以下内容:
void WINAPI CompletionRoutine(DWORD u32_ErrorCode, DWORD u32_BytesTransfered, LPOVERLAPPED pk_Overlapped)
{
if (u32_ErrorCode != 0)
printf("CompletionRoutine: Unable to write to file! Error: %u, AddrOverlapped: %p\n", u32_ErrorCode, pk_Overlapped);
else
printf("CompletionRoutine: Transferred: %u Bytes, AddrOverlapped: %p\n", u32_BytesTransfered, pk_Overlapped);
SetEvent(pk_Overlapped->hEvent);
}
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
printf("Compiled as: %d Bit\n", sizeof(DWORD_PTR) == 8 ? 64 : 32);
printf("Creating file\n");
const LPWSTR fileName = L"E:\\Temp\\Test.txt";
HANDLE h_File = CreateFileW(fileName, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, 0, CREATE_ALWAYS, FILE_FLAG_OVERLAPPED, 0);
if (h_File == INVALID_HANDLE_VALUE)
{
printf("Unable to create file! Error: %u\n", GetLastError());
return 0;
}
HANDLE h_Event = CreateEvent(NULL, TRUE, FALSE, NULL);
if (h_Event == NULL)
{
printf("Unable to create wait event! Error: %u\n", GetLastError());
CloseHandle(h_File);
DeleteFile(fileName);
return 0;
}
OVERLAPPED k_Over = {0};
k_Over.hEvent = h_Event;
printf("Writing to file (AddrOverlapped: %p)\n", &k_Over);
if (!WriteFileEx(h_File, "ABCDEF", 6, &k_Over, &CompletionRoutine))
{
printf("Unable to write to file! Error: %u\n", GetLastError());
CloseHandle(h_File);
DeleteFile(fileName);
return 0;
}
printf("Waiting for write to complete\n");
DWORD dwResult;
do
{
dwResult = WaitForSingleObjectEx(k_Over.hEvent, 5000, TRUE);
if (dwResult == WAIT_OBJECT_0)
{
printf("Write Completed\n");
break;
}
if (dwResult != WAIT_IO_COMPLETION)
{
if (dwResult == WAIT_TIMEOUT)
printf("Timeout waiting for write to complete!\n");
else
printf("Unable to wait for write to complete!\n");
CancelIo(h_File);
break;
}
}
while (true);
printf("Finished waiting\n");
CloseHandle(h_File);
CloseHandle(h_Event);
printf("Exit\n");
return 0;
}
请不要“乞求虫子”,尤其是在标题中。如果只是简单地陈述观察到的行为,而没有额外的评论,这个问题会更受欢迎。您现在满意了吗?您没有指定文件\u标志\u重叠
。从文档中:如果未指定此标志,则I/O操作将被序列化,即使对读写函数的调用指定了重叠结构。
谢谢!是的,当我添加文件\u标志\u重叠时,它可以工作!但是,多个操作系统中只有一个关心这个问题,这仍然很奇怪。你确定你尖叫了这个BUG!!!!够大声吗?如果你真的,真的很丢脸,说你发现了一个bug,然后发现你没有正确阅读文档,你就会知道你是这么做的。(故事的寓意是:千万不要假设你发现了数百万其他用户从未发现的bug,特别是在操作系统内部使用如此频繁的API函数中。要知道问题出在你的代码中的几率非常高。)你不明白问题出在哪里。使用CreateFile而不是CreateFileEx对我的情况来说不是一个解决方案。我没有说任何关于CreateFileEx()
。我将SleepEx()
替换为WaitForSingleObjectEx()
,这样更安全。但你是对的,我最初确实错过了根本原因——丢失的文件\u标志重叠了标志,我在看到后将其添加到了我的答案中。我发布的代码只是为了重现问题。显然,我删除了所有错误处理,以使其更具可读性。而SleepEx正是为了达到这个目的。没有必要更换它。试试看!您假设写操作将在1000毫秒内完成。也许在这个简单的示例中,它会完成。但在现实生产项目中,这是一个危险的假设。如果将OVERLAPPED
结构放在堆栈上,并且它在调用完成例程之前消失,则会发生不好的事情。声明:“重叠的数据结构必须在写入操作期间保持有效。在写入操作挂起完成时,它不应是可能超出范围的变量。”这就是为什么我的示例实际上在允许重叠的消失之前等待调用完成例程的原因。在更复杂的项目中,您通常会在堆上分配重叠的OVERLAPPED
,并让完成例程在完成使用后释放它。您没有检查writefilex()
,而是忽略完成例程报告的错误代码。在完成例程中,如果u32\u-bytesttransfered
中的u32\u-ErrorCode
不为0,则u32\u-bytesttransfered
将为0。在WinAPI中发现的错误:0,在代码中发现的错误:1(至少)。如果不立即责怪经过良好审查的代码,编程通常效率更高,尤其是当这种责怪与个人对其“应该”如何工作的想法混杂在一起时。检查程序员可能犯下的每一个错误不是API的责任。如果您不遵守规则(在本例中,WriteFilex的文档中非常清楚地描述了这些规则),任何事情都有可能发生,甚至可能看起来是可行的。错误就在您的代码中。Windows是正确的,其行为符合设计要求。我不明白你为什么这么难接受。