September 19th, 2023

Why did the 16-bit _lopen and _lcreat function return -1 on failure instead of 0?

Some time ago, I discussed why HANDLE return values are so inconsistent, and I traced it all the way back to the 16-bit _lopen and _lcreat functions, which returned -1 on failure.

But why do those functions return -1 on failure instead of zero?

The _lopen and _lcreat functions were Windows versions of the C runtime _open and _creat functions. The C runtime functions came in four different versions depending on which MS-DOS memory model you were using, and the convention was that when Windows adopted a C runtime function, it used the “large” version with the L prefix, since that is the most general version.

Okay, so why did _open and _creat return -1 on failure?

Because they were MS-DOS-compatible versions of the Unix functions open and creat. They even preserve the dropped silent “e” at the end of creat.

Okay, so why do those functions return -1 on failure?

On Unix, the return value is an integer that represents a file descriptor, valid file descriptors are integers starting with zero. Every process comes with three predefined file descriptors:

Descriptor Meaning
0 stdin (standard input)
1 stdout (standard output)
2 stderr (standard error)

Files opened by the program begin with file descriptor 3.

The value -1 is used to represent failure because 0 was already taken.

And that value of -1 carried forward, through a chain of backward compatibility, to Win32 as the numeric value of INVALID_HANDLE_VALUE. We saw a little while ago one of the consequences.

Topics
History

Author

Raymond has been involved in the evolution of Windows for more than 30 years. In 2003, he began a Web site known as The Old New Thing which has grown in popularity far beyond his wildest imagination, a development which still gives him the heebie-jeebies. The Web site spawned a book, coincidentally also titled The Old New Thing (Addison Wesley 2007). He occasionally appears on the Windows Dev Docs Twitter account to tell stories which convey no useful information.

3 comments

Discussion is closed. Login to edit/delete existing comments.

  • Neil Rashbrook

    Or at least, every Unix process assumes that it comes with three predefined file descriptors. There’s nothing to stop you writing something like foo <&- to launch a process without a STDIN handle, but the process might not appreciate it.

    • Kevin Norris · Edited

      It's easy to break the assumptions of Unix programmers. There are programs that change their behavior depending on the value of , usually because you're supposed to make one or more symlinks with different names. I have to wonder what those programs do if you pass an empty argument list (POSIX says the application "should" ensure that the binary name is given as , but in their base definitions, "should" indicates a portability recommendation rather...

      Read more
    • skSdnW

      xcopy.exe is not happy if you don’t give it a stdin.