Windows中代表无效句柄的值为什么会有 NULL 和 INVALID_HANDLE_VALUE 两个?

陪她去流浪 桃子 2015年09月01日 编辑 阅读次数:5045

闲着没事,叙叙旧而已~

可能每个搞过Windows开发的人曾经都对“无效句柄”为何会有两个值纳闷过,这到底是为嘛呢?其实这两个值都很特殊,一个是NULL(0),一个是INVALID_HANDLE_VALUE(-1,~0)。作为通用返回值来理解,NULL代表无效指针(当然句柄也就无效了,因为句柄实质上就是一个指针);而INVALID_HANDLE_VALUE其值为-1(或~0),这在类Unix中用来表示错误返回太常见了(Windows也是)。

但是为啥Windows两个都使用了呢?来看看这些“新鲜旧事物(Old New Thing,译错了)”。

如果你仔细观察那些返回句柄(Handles)的函数,在返回无效句柄时,她们有些返回NULL,像CreateThread,有些却返回INVALID_HANDLE_VALUE,像CreateFile。以至于你经常搞忘记她们谁才代表出错,所以你不得不经常去翻看MSDN。

原因很简单:由于历史原因。。。。

早期16位Windows程序中,当函数(像OpenFile, _lopen, _lcreate)出错时以返回-1来表示。所以,为了保持对16位程序的兼容性,32位程序中类似 CreateFile 的函数出错时就不得不返回 INVALID_HANDLE_VALUE 以方便从16位程序移植到32位了。

但是,在16位程序中并没有像 CreateThread 或 CreateMutex 这样的函数,所以不需要考虑兼容性,她们就选择返回 NULL 来代表失败。

于是就造成了分歧。由于失败的设计所造成的分歧,现在,如果出现了新的函数,她们要返回错误(失败)的句柄,她们也很难选择!

这种不统一(不连续性)会带来很多麻烦的问题:

  1. 第一,你必须小心仔细地检查这些函数的返回值,查看文档以便得知到底是NULL还是INVALID_HANDLE_VALUE才代表失败的返回;
  2. 第二,如果你在写一个通用的包装代码(像是句柄类),那么你必须考虑两种无效句柄的情形;
  3. 第三,在初始化一个无效句柄的值时,对于不同的函数,难以选择;
  4. 第四,对于 INVALID_HANDLE_VALUE,有时你还得格外小心,比如某些巧合:INVALID_HANDLE_VALUE 恰好是 GetCUrrentProcess() 返回的用来代表当前进程的伪句柄值!而且许多的内核对象会接受这些伪句柄!

好了,大概就是这些了!那么,该如何更好地处理?

我是这样做的:一视同仁,通通给我取 NULL 代表错误!另外有一个好处就是,NULL 在逻辑表达式中为FALSE,对于失败来说更符合逻辑!

比如:

HANDLE hFile = CreateFile(...); // 得到句柄
if(hFile == INVALID_HANDLE_VALUE) hFile = NULL; // 统一化
if(!hFile) { // 正式的错误处理
    ...
}

标签:WinAPI · why