Why are HANDLE return values so inconsistent? - The Old New Thing (microsoft.com)https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443
Raymond Chen 2004年01月27日
简介
在处理 Windows 编程中的句柄时,开发者需要面对的一个挑战是不同函数可能返回不同的错误值。这种不一致性源于对16位Windows系统的兼容性考虑,以及随着时间的推移,新函数的添加并没有统一的返回值标准。因此,开发者在编写代码时必须小心谨慎,确保正确处理函数返回的句柄值,避免潜在的错误和资源泄漏。
正文
在查看各种返回句柄(HANDLE)的函数时,你会发现它们的行为并不统一:有些函数在失败时返回 NULL(如 CreateThread),而另一些则返回 INVALID_HANDLE_VALUE(如 CreateFile)。你必须查阅具体函数的文档,以了解它们在失败情况下的返回值。
这种不一致性的原因,可能正如你所猜测的,是出于历史兼容性的考虑。这些返回值的选择是为了与16位的Windows系统兼容。在16位的Windows中,OpenFile、_lopen 和 _lcreat 等函数在失败时返回 -1,因此32位的 CreateFile 函数为了便于从16位Windows代码迁移,选择返回 INVALID_HANDLE_VALUE。
此外,由于没有16位的Windows等效函数,CreateThread 或 CreateMutex 等函数则返回 NULL。由于这种不一致性已经成为一种惯例,每当新增函数时,它们是返回 NULL 还是 INVALID_HANDLE_VALUE 都有些随机。
这种不一致性带来了几个后果:
- 你必须仔细检查每个函数的返回值。
- 如果你编写一个通用的句柄包装类,你需要考虑到两种“非句柄”值的可能性。
- 如果你想要预先初始化一个 HANDLE 变量,你必须以与你打算使用的函数兼容的方式进行初始化。例如,以下代码是错误的:
HANDLE h = NULL;
if (UseLogFile()) {h = CreateFile(...);
}
// 执行其他操作
if (h) {Log(h);
}
// 执行其他操作
if (h) {CloseHandle(h);
}
这段代码有两个错误:首先,它错误地检查了 CreateFile 的返回值,代码应该检查 INVALID_HANDLE_VALUE 而不是 NULL。其次,变量 h 的初始化也是错误的。以下是修正后的版本:
HANDLE h = INVALID_HANDLE_VALUE;
if (UseLogFile()) {h = CreateFile(...);
}
// 执行其他操作
if (h != INVALID_HANDLE_VALUE) {Log(h);
}
// 执行其他操作
if (h != INVALID_HANDLE_VALUE) {CloseHandle(h);
}
4.你必须特别注意 INVALID_HANDLE_VALUE 值:由于巧合,INVALID_HANDLE_VALUE 的数值恰好等于 GetCurrentProcess() 返回的伪句柄。许多内核函数可以接受伪句柄,因此如果你错误地使用了一个失败的 INVALID_HANDLE_VALUE 句柄调用,例如 WaitForSingleObject,你实际上将在自己的进程上等待。这个等待当然不会完成,因为一个进程在退出时会被置为已信号,所以你最终是在等待自己结束。