The CreateFile
function has five different creation disposition values. Let’s organize them.
If a file of the requested name doesn’t already exist, you have two choices: You can fail the operation, or you can create a new file.
If a file of the requested name already exists, you have three choices: You can fail the operation, you can open the existing file and preserve the existing data, or you can open the existing file but set the file length back to zero (truncate), discarding any existing file contents.
Now that we see the possibilities, we can make our table.
If file exists | If file doesn’t exist | |
---|---|---|
Fail | Create | |
Fail | CREATE_NEW |
|
Open | OPEN_EXISTING |
OPEN_ALWAYS
|
Truncate | TRUNCATE_EXISTING |
CREATE_ALWAYS |
“Hey, wait, I found a hole in your API. You missed a case: The upper left box is empty!”
That’s right, but the upper left box is “Fail if the file exists, and fail if the file doesn’t exist.” If we had to give that box a name, it would be something like FAIL_ALWAYS
.
Which isn’t particularly useful.
If it’s that important to you, I guess you can simulate it yourself:
HANDLE CreateFileFailAlways() { SetLastError(ERROR_OPEN_FAILED); return INVALID_HANDLE_VALUE; }
+3DOS on the Spectrum +3 has two different parameters to its file open call: one for what to do if the file doesn’t exist { fail / create new file } and one if it does { fail / open file / truncate file / rename existing file to .BAK and create new file }
Unix equivalent:
CREATE_NEW is O_CREAT | O_EXCL.
OPEN_EXISTING is the default (no flags required, except as noted below).
OPEN_ALWAYS is O_CREAT
TRUNCATE_EXISTING is O_TRUNC
CREATE_ALWAYS is O_CREAT | O_TRUNC
FAIL_ALWAYS does not exist, but on systems where O_EXEC != O_SEARCH, it can be emulated with O_DIRECTORY | O_EXEC (it's not legal to execute a directory). Note that O_TRUNC | O_RDONLY is not a standards-compliant emulation, because the standard says that's UB.
These must be combined with exactly...
I like Windows variation more. It’s way more readable.
It would stand to reason if those values were organized as two bit fields, like one bit for the disposition for the “does not exist” case and two for “exists”, but no. They are defined as 1..5 in somewhat arbitrary order.
“Hey, wait, I found a hole in the API that might plug that other hole. Let’s define FAIL_ALWAYS as 0!”
But that would probably fail with the wrong reason, setting last error not to ERROR_OPEN_FAILED but...
The MS-DOS Extended Open/Create function INT 21h AX=6C00h does take those as two bit fields. Is that older or newer than CreateFile?
MS OS/2 apparently has DosOpen and DosOpen2; in both of them, the bit fields of the OpenFlag parameter are identical to what Extended Open/Create takes in MS-DOS 4.
Almost certainly older than CreateFile, but it depends on whether CreateFile was part of the OS/2 API.
Hey I actually had that phantom sixth disposition come up once. I worked around it by calling CreateFile immediately followed by CloseHandle.
Your simulation doesn’t work. I needed to know wither it’s ERROR_FILE_EXISTS, ERROR_ALREADY_EXISTS (there’s a directory by that name…), ERROR_FILE_NOT_FOUND or ERROR_PATH_NOT_FOUND.
FindFirstFile should handle all of those.