Date: | March 2, 2004 / year-entry #79 |
Tags: | history |
Orig Link: | https://blogs.msdn.microsoft.com/oldnewthing/20040302-00/?p=40443 |
Comments: | 21 |
Summary: | 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... |
If you look at the various functions that return 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
(Armed with this, you can now answer the following trivia
question: Why do I call
On the other hand, there are no Win16 equivalents for
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 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 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
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
|
Comments (21)
Comments are closed. |
I would have guessed it was called "CreateFile" instead of "OpenFile" because it creates a file handle or object.
My guess would have been because the same function can both create AND open files, thus settled for the CreateFile name.
(It’s also a parallel construction with the other handle-creation functions like CreateMutex, CreateEvent… But it’s still confusing.)
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.
I don’t quite understand how CreateFile had to return -1 to "facilitate porting code from Win16". CreateFile didn’t exist in Win16.
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.
Ahh, I see.
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.
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?
? You were never able to interchange file handles and other types of handles (like GDI handles). Any code that did was already broken.
To use an HFILE, one must call the 16-bit compatibility function OpenFile. Why does CreateFile return a generic HANDLE instead of an HFILE?
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).
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.
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.
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.
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
Right, now that we’ve got the basics of reliability covered, we can move on to SafeHandle. If you’ve…
It’s a confusing story.
(Yes, Betas). While my previous post talked only about the past, this post talks about the present. Some of the…