Welcome to MSDN Blogs Sign in | Join | Help

Why are HANDLE return values so inconsistent?

If you look at the various functions that return HANDLEs, you'll see that some of them return NULL (like CreateThread) and some of them return INVALID_HANDLE_VALUE (like CreateFile). You have to check the documentation to see what each particular function returns on failure.

Why are the return values so inconsistent?

The reasons, as you may suspect, are historical.

The values were chosen to be compatible with 16-bit Windows. The 16-bit functions OpenFile, _lopen and _lcreat return -1 on failure, so the 32-bit CreateFile function returns INVALID_HANDLE_VALUE in order to facilitate porting code from Win16.

(Armed with this, you can now answer the following trivia question: Why do I call CreateFile when I'm not actually creating a file? Shouldn't it be called OpenFile? Answer: Yes, OpenFile would have been a better name, but that name was already taken.)

On the other hand, there are no Win16 equivalents for CreateThread or CreateMutex, so they return NULL.

Since the precedent had now been set for inconsistent return values, whenever a new function got added, it was a bit of a toss-up whether the new function returned NULL or INVALID_HANDLE_VALUE.

This inconsistency has multiple consequences.

First, of course, you have to be careful to check the return values properly.

Second, it means that if you write a generic handle-wrapping class, you have to be mindful of two possible "not a handle" values.

Third, if you want to pre-initialize a HANDLE variable, you have to initialize it in a manner compatible with the function you intend to use. For example, the following code is wrong:

HANDLE h = NULL;
if (UseLogFile()) {
    h = CreateFile(...);
}
DoOtherStuff();
if (h) {
   Log(h);
}
DoOtherStuff();
if (h) {
    CloseHandle(h);
}
This code has two bugs. First, the return value from CreateFile is checked incorrectly. The code above checks for NULL instead of INVALID_HANDLE_VALUE. Second, the code initializes the h variable incorrectly. Here's the corrected version:
HANDLE h = INVALID_HANDLE_VALUE;
if (UseLogFile()) {
    h = CreateFile(...);
}
DoOtherStuff();
if (h != INVALID_HANDLE_VALUE) {
   Log(h);
}
DoOtherStuff();
if (h != INVALID_HANDLE_VALUE) {
    CloseHandle(h);
}

Fourth, you have to be particularly careful with the INVALID_HANDLE_VALUE value: By coincidence, the value INVALID_HANDLE_VALUE happens to be numerically equal to the pseudohandle returned by GetCurrentProcess(). Many kernel functions accept pseudohandles, so if if you mess up and accidentally call, say, WaitForSingleObject on a failed INVALID_HANDLE_VALUE handle, you will actually end up waiting on your own process. This wait will, of course, never complete, because a process is signalled when it exits, so you ended up waiting for yourself.

Published Tuesday, March 02, 2004 6:57 AM by oldnewthing
Filed under:

Comments

# CreateFile vs OpenFile

Tuesday, March 02, 2004 7:34 AM by Karl Koscher
I would have guessed it was called "CreateFile" instead of "OpenFile" because it creates a file handle or object.

# re: Why are HANDLE return values so inconsistent?

Tuesday, March 02, 2004 7:46 AM by Mario Goebbels
My guess would have been because the same function can both create AND open files, thus settled for the CreateFile name.

# re: Why are HANDLE return values so inconsistent?

Tuesday, March 02, 2004 7:57 AM by Raymond Chen
(It's also a parallel construction with the other handle-creation functions like CreateMutex, CreateEvent... But it's still confusing.)

# re: Why are HANDLE return values so inconsistent?

Tuesday, March 02, 2004 8:00 AM by Jack Mathews
And it'll stay this way forever, because Win64 needs to be compatible to Win32 needs to be compatible to Win16... You know, making all of this "compatibility" between the different versions of Win## just makes it harder and harder to master over the years.

# re: Why are HANDLE return values so inconsistent?

Tuesday, March 02, 2004 8:23 AM by edmundo
> And it'll stay this way forever, because Win64 needs to be compatible to Win32 needs to
> be compatible to Win16

ad nauseum until Windows becomes such a kludge of hacks that it becomes impossible for Microsoft to maintain. (is this already happening?)

# re: Why are HANDLE return values so inconsistent?

Tuesday, March 02, 2004 10:11 AM by Jordan Russell
I don't quite understand how CreateFile had to return -1 to "facilitate porting code from Win16". CreateFile didn't exist in Win16.

# re: Why are HANDLE return values so inconsistent?

Tuesday, March 02, 2004 10:17 AM by Raymond Chen
In the hopes of convincing people to switch to Win32, there was a program called PORTTOOL.EXE that took a Win16 source program and ported it to Win32 (using various rewrite rules and heuristics). I suspect the return value allowed PORTTOOL.EXE to port various 16-bit file open calls to CreateFile without having to worry about trying to understand the error-checking logic.

# re: Why are HANDLE return values so inconsistent?

Tuesday, March 02, 2004 10:23 AM by Jordan Russell
Ahh, I see.

# re: Why are HANDLE return values so inconsistent?

Tuesday, March 02, 2004 12:03 PM by Mike Dimmick
edmundo: it's certainly possible to keep compatibility shims up in the top layers of the software stack, keeping the lower levels pretty clean and maintainable. If you've written your software well, with decent use of abstraction and information-hiding, and evaluate your maintenance fixes correctly, you can vary the implementation behind your interfaces without affecting the client of that interface.

If the semantics of the interface remain the same, there's no need to change it. On the contrary, change for change's sake is almost never good in any software, least of all platform software. You leave yourself with no product to sell until you're finished, and no ISV can sell their product until you're finished either. Even then they can only sell to people who've already bought your new platform. I'm already finding myself wondering whether Longhorn will actually be released, or whether it will become another Cairo.

# re: Why are HANDLE return values so inconsistent?

Tuesday, March 02, 2004 5:07 PM by Norman Diamond
> some of them return INVALID_HANDLE_VALUE
> (like CreateFile).

That is sometimes true and sometimes false, which is exactly why I asked about it when you coded an insufficient test. Under Windows 98, CreateFile sometimes returned INVALID_HANDLE_VALUE and sometimes returned NULL (and sometimes returned valid handles :-) I did not try to reproduce it under Windows XP, but continue to code defensively.
if ((MyHandle == INVALID_HANDLE_VALUE) || (MyHandle == NULL)) ...

# re: Why are HANDLE return values so inconsistent?

Tuesday, March 02, 2004 5:53 PM by Norman Diamond
Sorry for adding a second message in a row, but it's not my fault no one else posted in between :-)

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/sdkintro/sdkintro/strict_compliance.asp

> General Requirements
[...]
> You cannot use one handle type where another
> is expected.
[...]
> For best results, the generic HANDLE type
> should be used only when necessary.

I agree. But to do so for file handling, we have to call a 16-bit compatibility function. Why did Win32 break compatibility with this formerly respected practice?

# re: Why are HANDLE return values so inconsistent?

Tuesday, March 02, 2004 5:55 PM by Raymond Chen
? You were never able to interchange file handles and other types of handles (like GDI handles). Any code that did was already broken.

# re: Why are HANDLE return values so inconsistent?

Tuesday, March 02, 2004 6:30 PM by Norman Diamond
To use an HFILE, one must call the 16-bit compatibility function OpenFile. Why does CreateFile return a generic HANDLE instead of an HFILE?

# re: Why are HANDLE return values so inconsistent?

Tuesday, March 02, 2004 6:35 PM by Raymond Chen
Because that's what CloseHandle and ReadFile and WriteFile use. File handles are a special type of kernel handle, and kernel handles are manipulated as HANDLE.

I don't understand why you are calling 16-bit compatibility functions (that use HFILE - which if you look is just "int"). Use ReadFile and WriteFile.

The purpose of that remark ("the generic HANDLE type should be used only when necessary") is to get people to stop using HANDLE for things that aren't kernel handles (like GDI and USER handles).

# re: Why are HANDLE return values so inconsistent?

Tuesday, March 02, 2004 6:48 PM by Norman Diamond
I am not calling 16-bit compatibility functions for file access (except for application-specific .ini files as mentioned in the other thread). My question here was why Win32 took a backwards step from Win16. MSDN Platform SDK documentation recommends practices which Win16 got right in this case.

# re: Why are HANDLE return values so inconsistent?

Tuesday, March 02, 2004 6:51 PM by Raymond Chen
It was a backwards step in the sense that what used to be a separate type (HFILE) is now a generic HANDLE. (Of course, that separate type was just "int" so it wasn't really a separate type.)

It was a step forward in the uniform handling of kernel objects.

Engineering is all about tradeoffs.

# re: Why are HANDLE return values so inconsistent?

Wednesday, March 31, 2004 9:05 AM by DrPizza
It isn't a backwards step.

One of the remarkable things about the Win32 API (which is generally far from remarkable) is that it's quite polymorphic in this area. In general, a HANDLE is the same wherever it's from.

This is great. It means I don't have to care if a HANDLE is a file, a thread, a process, a mutex, an event, a pipe, a directory, a physical disk, a volume, a mailslot, a console, a serial port, or a tape drive -- I can do certain operations (such as close it or wait on it) in the same way.

Obviously, not all combinations are meaningful; ReadFile doesn't (at least, I assume it doesn't, as it has no obvious behaviour) work with a mutex handle, for example. But it's still pretty good -- it's more versatile than POSIX, for example.

# re: Why are HANDLE return values so inconsistent?

Tuesday, April 06, 2004 5:57 PM by Norman Diamond
3/31/2004 9:05 AM DrPizza:

> It isn't a backwards step.

The I trust you'll fix this MSDN page:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/sdkintro/sdkintro/strict_compliance.asp

# SafeHandle (Reliability Part 2)

Friday, May 26, 2006 11:40 AM by Coding Sanity
Right, now that we've got the basics of reliability covered, we can move on to SafeHandle. If you've...

# What's the difference between WINVER, _WIN32_WINNT, _WIN32_WINDOWS, and _WIN32_IE?

Wednesday, April 11, 2007 1:43 PM by The Old New Thing

It's a confusing story.

# Enthusiastic Trepidation

Monday, February 25, 2008 1:34 AM by Unsanity.org

(Yes, Betas). While my previous post talked only about the past, this post talks about the present. Some of the...

New Comments to this post are disabled
 
Page view tracker