How can I check errors when opening file?

Hi. I am using std::ofstream to write a log file. I know I can check if there was an error during opening or writing to the file by checking for failbit or badbit, but that doesn't give me any information to what actually happened, only that something happened. How can I check and detect what actually happened when opening the file or writing to it? I am using Visual C++ 6.0 under Windows 98 SE, a guest OS on VMware Workstation 15 (non-commercial/non-profit licence) hosted by my Windows 10 64bit machine.

I even prepared my own error codes in errcodes.h file:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/***********************************
 * Kris-Kros Game Kit
 *----------------------------------
 * Kris-Kros Map Generator source
 * File: errcodes.h
 *
 * Made by Marek Poláček
 * Github.com/Polda18
 *
 * GNU-GPL v3.0
 * 2019
 ***********************************/

#ifndef ERRCODES_H
#define ERRCODES_H

#define ERR_ARG_INVALID               1
#define ERR_FILE_FORMAT_INCORRECT     2
#define ERR_FILE_NOT_FOUND            3
#define ERR_FILE_WRITE_PROTECTED      4
#define ERR_FILE_DAMAGED              5
#define ERR_FOLDER_NOT_FOUND          6
#define ERR_FOLDER_WRITE_PROTECTED    7
#define ERR_FOLDER_DAMAGED            8
#define ERR_INSUFFICIENT_PERMISSION   9
// Next error codes based on requests

#define ERR_UNEXPECTED               99
// Unexpected error, return code may be raised if reached in future (highly unlikely)

#endif 


And a function to write a log file, now yet without the file write function to it, in logger.h and logger.cpp files:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/***********************************
 * Kris-Kros Game Kit
 *----------------------------------
 * Kris-Kros Map Generator source
 * File: logger.cpp
 *
 * Made by Marek Poláček
 * Github.com/Polda18
 *
 * GNU-GPL v3.0
 * 2019
 ***********************************/

#include <iostream>
#include <fstream>
#include <string>
#include <ctime>
#include <errno.h>

#include "errcodes.h"
#include "commands.h"
#include "logger.h"

int mapgen::logger::log(std::string lFname, std::string lMsg)
{
    int rCode = 0;

    std::string lFullFname = lFname + ".log";

    time_t raw;
    time(&raw);

    struct tm *tInfo;
    char buff[80];

    timeinfo = localtime(&raw);
    strftime(buff, sizeof(buff), "%Y-%m-%d %H:%M:%S", tInfo);

    std::string now(buff);
    // [YYYY-MM-DD HH:MM:SS] <lMsg>

    // TODO: write into log file

    return rCode;
}


My question is: how can I get the exact reason of failure if it happens? I want it to work under Windows 9x, simply because I want it to work under pretty much entire Windows versions span on 32bit architecture. Applications built under and for Windows 9x work under all versions of Windows, even on 64bit Windows 10, latest build. This program I am building (Kris-Kros Map Generator - mapgen.exe) is part of my game development, Kris-Kros. It's all in C++

Entire source code can be found on GitHub:
https://github.com/Polda18/Windows-Kris-Kros-Game
Last edited on
On file open errors

You're asking about a kind of no-man's land here. The fstreams deal with that generally applicable to the language generally, whereas failure of file opening or subsequent operation are OS specific, and so were generally ignored by the design. From the view of the fstream, the file either opens or it doesn't. Diagnostics is outside the domain.

For Windows the associated diagnostic messages come from the "GetLastError" function, which refer to a numeric value in a list of several thousand messages, only some of which apply to files. *Nix handles things a bit differently, using a 'global' "errno" value which does something similar to GetLastError, and may work to provide the same result with a slight bit of compatibility over the two platforms.

That brings me to the entire point of portability before I move on to other ideas on the question.

Portability between 32 and 64 bit Windows is simple to achieve in modern development tools like Visual Studio, though targeting Windows 95 makes little sense in the modern era, and Visual Studio 6.0 is way too behind in language feature to be applicable to any modern development.

To that end, also, it makes little sense at this point to consider portability between versions of Windows without also considering portability beyond Windows. The true value of portability is user option, and there are few options open to users within the Windows domain.

For example, in modern C++ there is a std::filesystem library (C++17), which is well over 20 years beyond Visual Studio C++, and works on *Nix and Windows reasonably well to perform the kind of diagnostics you're asking about.

One typical example comes from a very old small problem when creating a file - if the user specifies a full path, but the path and several leaves of the path don't exist, it was quite a chore to write a means by which each missing path was built before the file could be created. For example, if I assume there is a directory "c:\temp", but the intent is to fashion a file in "c:\temp\appname\data\filename.dat", the directory "appname" would have to be created first, then the directory "data" within it, before making the file itself.

The std::system library has mechanisms to do that automatically. It will never be available to Visual Studio 6.0.

However, creating 32 bit win32 applications in recent Visual Studio (free versions) is still well supported, yet with modern C++ advantages you should seriously consider. Much of the "portability" concerns you mention aren't a problem, but what is a serious potential problem are both reliability and security issues related to all code generated by Visual Studio 6.0. It is still possible to target applications in recent VS builds that will run on older 32 bit Windows, but with the added option, due to modern C++, of working on 32 and 64 bit Linux, Android, iOS, OS X, and others.

Bottom line is that there are no really good ways to use fstreams to get diagnostic information outside of "errno" or "GetLastError", and you probably need more investigative and proactive approaches like those from std::filesystem.

For example, before even opening the stream, check if the file exists and is a standard file (if reading). If not, the diagnostics from std::filesystem say why (a parent directory in the path doesn't exist, either, for example).

Further, if the solution involves helping the user to search, std::filesystem has portable means of searching directory paths.

If writing a file, creating one becomes it's own set of issues. Making sure the prerequisite directories exist, or are fashioned as required, and checking permissions (or setting them as required), are all part of the std::filesystem.

Then, checking for available space as an issue, or finding more space elsewhere on other drives - all possible with std::filesystem - are simply not in the domain of an error taken from an attempt to open a file.






Well, to put it simply, I work with Visual Studio 6.0 on Windows 98 because I am fan into old systems and it brings so much nostalgic value to me. And believe it or not, there are still lots of people who use Windows 98 on daily schedule, and not just a OS within an OS, actually they use it on a physical machine that may or may not be connected to the Internet. Windows 98 may be quite outdated and it may have various reasons why not to use it, but I like the simplicity of this edition of Windows. And for portability issue - do you know Total Commander? If Windows 98 is truly lost era of Windows, why do Total Commander still target Windows 9x? And I'm talking about latest builds. One of the few modern programs, that exist more than 20 years, and their compatibility is spanning from Windows 95 all the way up to latest builds of Windows 10. And still under quite heavy development.

As for portability to Linux, I leave that up to fans and other developments to make Linux friendly forks. I am making Windows only game, retro styled and I intentionally target Windows 9x, because I like to learn old methods of programming. Believe it or not, I'd also like to make MS-DOS apps, I'd like to make a game that runs under MS-DOS...

EDIT: And to the modern Visual Studio versions: I tried to set up targetting to older Windows, at least Windows XP. The only targetted OS I could set is various builds of Windows 10. That's also the reason why I moved to older Visual Studio and older Windows, in order to ensure that the application will run under Windows 9x.

EDIT 2: https://docs.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror
Minimum supported client Windows XP [desktop apps | UWP apps]

Is there Windows 9x friendly function? Or do all functions in MSDN refer to NT editions of Windows only?
Last edited on
yes, we opened files on win 98 too. It was tough, but we managed :P

kidding aside...
ofstream works just fine on win98. windows has its own file tools as well; you can use MFC's file dialog box and such if you are doing gui stuff and you can hide these (that is, you can tap the functionality of the dialog box without making it visible to the end user) and you can reskin them for games as well if you want to go there.

If I recall the windows tools can tell you what you are asking about why a file failed to open in more detail. this comes at a cost of microsoft convolution (their tools have a lot of overhead, renaming of standard types (it has literally 50 versions of int and void pointer) and weirdness about getting pointers to instances of magic os things.

there is only so much that can go wrong with a file, though. most of it you can also self diagnose.
- its not there (name or path is wrong, or its gone).
- its corrupted (its there, but the OS or disk can't handle what is left of it from damage)
- its protected (you can't access it or the folder)
- its locked (some other program is blocking you from writing to it. reading is always permitted if I recall how share.exe and its offspring worked). You can override this with some hackery IIRC (unix is less tolerant than old win on this kind of thing).

thats all I can even think of for broad failures.
Last edited on
GetLastError should work in Windows 95+, though I don't have a computer to test that out :P

PS: the first "NT" version of Windows was Windows 3.1, so it's not a distinct set from Windows 9x.

________________________________________________

A lot of Win32 API functions should still work in Windows 95/98, even if the official MSDN documentation says it's no longer supported. You just have to be careful.

See: http://www.netchain.com/api/functions/createfile.html
For example, when using CreateFile in Windows 95, you have to make sure you pass in 0/null for the Security Attributes parameter.

CreateFile is probably worth checking out. (It can open and create files, despite the name.)
Last edited on
A lot of Win32 API functions should still work in Windows 95/98


I'd take that one step further.

Win32 API was originally implemented by Windows 95, then a few months later by Windows NT 3.1, and before that there was Win32S, a DLL for Windows 3.11 (16 bit) which allowed one to run Win32 single threaded programs for testing while awaiting the release of Windows95.

Put backwards, writing to the win32 API means the target should work on Windows95.
I'd have to try a VM of Windows95 or 98 to know if a modern version of Visual Studio would still work without issue.
@jonnin:
Well, I know that Windows have such utility, what I didn't know is that also Unix has.

I ended up using errno and writing my own error handler...

@Ganado:
Are you sure? Well, I needed to use more generic way for console application. But GUI is also part of the project. So if this is true, then I might use that one with GUI applications.

@Niccolo:
Wow, I didn't know that Microsoft made a way to test Win32 programs in 16bit environments. Thanks for sharing a bit of history :)
On win NT, if you were not around or did not know..
the first NT was, as said, a bit dos-win like.
the second NT (4.0, I think?) was win 98 like but the way it works prevented using directx and your ability to code games with graphics for it was severely limited. A lot of things simply would not work. It could be hacked to use the first couple of versions of directx but it was too far behind what the other branch of windows had... it could do some older games by that point but not the newer ones. It was a poor choice for anything much more interesting than solitaire.

They merged these 2 forks, I think that was win7 or win2000 whichever came first.
That used the HAL & security ideas of NT with the directx and approved hardware interactions of 98 into what we have today.
@jonnin:
Well, that's shocking. Windows NT and it's worse than Windows 98? :D Wow! You are right. I wasn't around back then. Well, my first contact with PC was in 1999 or 2000 rather. Before that, we had an Atari borrowed. We had an MS-DOS machine first, with a commander installed (because I do not remember a command line on that computer actually) - which one, I can't recall. Back then, I didn't even know that MS-DOS doesn't normally look like this - a blue screen with two windows with a dir structure, so I naturally didn't seek for the commander program name. And now I can't recall how it actually looked like and search for the exact commander. I remember that my uncle had the same commander, but his computer had mouse. Second computer we had (well, it was still the first one, but we upgraded) was Windows 95 machine, then Windows 98. This computer was then upgraded to Windows 2000 and given to my brother. Then we had Windows XP machine (around 2003) and later upgraded to Windows 7 when XP support ended. Now we have Windows 10. I was born 1996, so I guess I didn't have much opportunity to get in touch through all Microsoft newest products first handed. As a matter of fact, we didn't have an Internet connection until 2005. So I kind of lost track of 90's Internet era :/

If what you say is true, then why was Windows NT so much advertised? I guess that Windows NT was more-less designed for office work? Windows NT was more like a Proffessional edition of Windows, for servers or office work. That was changed with Windows 2000 that came in Proffessional edition for client office machines and in Server edition for server machines.

Early computers were not optimised for games, they were optimised for work. If you wanted to play games, you lauched up gaming console instead: Atari, NES; or an older BASIC computer: ZX-Spectrum, Commodore, etc. Computer wasn't supposed to play games with them, they were supposed to work with them. At least that was original intention. Then someone came with an idea of what would happen if someone wrote and programmed a game and started to sell them. It gained popularity.
NT had some business use improvements, it took a crack at security, had a better file system, and some other unix-already-found-this-out-before 'discoveries' and 'improvements'. It wasnt for home users or gamers.

2000 or whatever merged the NT and 98 branches so you got the best of both after that.

At that time, we were coming off win95 which was an unstable, disk thrashing, poorly developed nightmare. NT was awfully attractive to business users.

actually by this era (say, 93ish), PC gaming was in its heyday. Dos gave us wolf-3d, doom, xwing, and other amazing 3-d games by the end of its life; console was still mostly 2-d like mario. Atari was probably better than the early x86. The thing was that a cartridge had very limited space, a hard drive had much more, so PC overtook console until the consoles put disk drives in (mid/late 90s trying to get back in the game due to PC domination). The last game I remember beating into submission on dos 6.22 was heretic, which was made for win95 but you could force it if you knew the systems.

I went from my 386 to NT 4 (oops) to 2000 to to XP to 7 to winx if I remember the chain right. I did not get a computer until I was an adult, so I knew the older stuff but did not have it at home. You could dollar for dollar buy 3 solid (not gaming) systems now for what I paid for that 386, and if you factor in inflation, maybe 5 solid systems. My 396 did not have a FPU!
Last edited on
Topic archived. No new replies allowed.