NO

Author Topic: Returned error values....  (Read 10314 times)

CommonTater

  • Guest
Returned error values....
« on: March 29, 2012, 06:26:42 AM »
Many years into Windows programming and I still have to experiment with stuff constantly to find out which functions return which error values and short of causing hardware failures (not good for the computer!) I'm betting I've only discovered a fraction of them...
 
Yes I know the SDK gives the return values for all functions... but that's not enough in a lot of cases...
For example:  GetFileAttributesEx()  function returns 1 if it works or 0 if it fails... but if it fails we are supposed to call GetLastError() to find out what went wrong...
 
Ok, so which error values will I get from GetLastError() if GetFileAttributesEx() screws up?  Can it tell me if the failure was because the file was not found, or a disk error, or a sleeping network host?  This is simply not documented anyplace that I can find.
 
Does anyone know of a comprehensive list of which errors each function reports?
 
(EDIT: The reason I ask is that I'm presently messing with some code where the error value determines what I have to do next... )

 
« Last Edit: March 29, 2012, 06:38:24 AM by CommonTater »

Offline Bitbeisser

  • Global Moderator
  • Member
  • *****
  • Posts: 772
Re: Returned error values....
« Reply #1 on: March 29, 2012, 08:48:34 AM »
The error codes you get back by GetLastError() depend on the function that is/was causing the error. And not all functions do return more detailed error codes in case of a failure...
There is no generalized error code across the whole system (Windows).

Ralf

Offline Stefan Pendl

  • Global Moderator
  • Member
  • *****
  • Posts: 582
    • Homepage
Re: Returned error values....
« Reply #2 on: March 29, 2012, 09:33:02 AM »
The file managing APIs are mostly returning the old DOS error codes, which are documented at MSDN.
---
Stefan

Proud member of the UltraDefrag Development Team

CommonTater

  • Guest
Re: Returned error values....
« Reply #3 on: March 29, 2012, 12:04:35 PM »
Thanks for the replies guys... but I already knew about the DOS codes on MSDN...

It's not a question of what the error codes are... I have that, both in the WinErr program and the MSDN listings. 

Lets see if I can't give you a better example...

As I mentioned above I'm currently trying GetFileAttributesEx() in a program I've written... I know that GetLastError() returns ERROR_FILE_NOT_FOUND if the specified file doesn't exist and ERROR_DEVICE_NOT_READY for an empty card reader slot and a few others...  But can it tell the difference between not finding the file and some nasty hardware problem?
 
It's not the usual matter of simply posting an error report... "Error XXX when reading attributes"...
I need to take action on the error itself...
Like this...
Code: [Select]
if (!GetFileAttributesEx(FileName,&atdata))
  switch (GetLastError)
    { case ERROR_FILE_NOT_FOUND :
            ReportMissingFile(FileName);
            break;
      case ERROR_DEVICE_NOT_READY :
            AskForCard();
            break;
      case ...
    }

Same thing with CopyFileEx() ... can it tell the difference between not finding the file to copy and a read error from disk and a network host that's in standby?

Unfortunately this project demands I know specifically which errors each of these functions can and cannot report.



 
« Last Edit: March 29, 2012, 12:08:17 PM by CommonTater »

aardvajk

  • Guest
Re: Returned error values....
« Reply #4 on: March 29, 2012, 04:50:15 PM »
Then your project is uncompleteable. There is no way to determine what errors they return in what situations. An exhaustive list of errors would be a maintenance and documentation nightmare, and that's just if Microsoft's code ran in the storage stack. What of third party drivers?

Not only that, but as they're an implementation detail over which you have no control. What if Windows 8 detected the card started returning SCARD_E_NO_ACCESS (a valid error in winerror.h) instead of ERROR_DEVICE_NOT_READY for CopyFile? Now what are the existing versions of your program going to do when they encounter this new error? Whatever you would do for that situation, that's what you should do now.

Consider that there are approximately 4,800 error codes in Win7's winerror.h, then a further 2,000 in ntstatus.h (the error codes drivers use) with Win8 no doubt adding more. It's folly to switch in the returned errors.

Here's what a Microsoft employee says about it

CommonTater

  • Guest
Re: Returned error values....
« Reply #5 on: March 29, 2012, 07:27:00 PM »
Thank you for pointing me to that article, aardjvajk ... it was quite helpful.

However; if something as simple as this can render a relatively straight forward recursive search operation "uncompletable" (== Impossible?) then I'd have to join the initial commentors on that article in complaining that MS has some serious problems in their API...

For example:
Code: [Select]
int IsFileHidden(PWCHAR Path)
  {  DWORD rv;
     rv = GetFileAttributes(Path);
 
     // trap on errors
     if (rv == INVALID_FILE_ATTRIBUTES)
       { if ( GetLastError() != ERROR_FILE_NOT_FOUND)
            { wprintf(L"Fatal error accessing  %ls\n\n",Path);
               exit(-1); }
         else
            return -1; }
 
     // test hidden bit
     if  (rv & FILE_ATTRIBUTE_HIDDEN) > 0)
       return 1;
     else
       return 0; }
In my books this is common sense error handling... fix what you can, report what you must... when in doubt: bail.
 
Is it not obvious that a function like GetFileAttributes() has at least four possibilities... the attribute you are looking for is 0, the attribute is 1, the file doesn't exist or something went terribly wrong... To not be able to trap that reliably, renders the entire Windows API untrustworthy in ways that cannot be corrected.
 
And what of functions that *count* on errors ... for example the PathFileExists() function which clearly relies on the "File not found" error to do it's magic.  ::)
 
I usually write code that simply bails when things don't go as planned (like most people do).  This is the first time I've had to actually make decisions based on which error occurs... and I gotta say this is just a tad unsettling...
 
I also find it rather puzzling that according to the SDK  GetLastError() was not introduced until Windows XP ....
 
 
« Last Edit: March 29, 2012, 07:55:47 PM by CommonTater »

Offline Stefan Pendl

  • Global Moderator
  • Member
  • *****
  • Posts: 582
    • Homepage
Re: Returned error values....
« Reply #6 on: March 30, 2012, 09:55:16 AM »
I think you will have to use trial and error to be able to act on an error accordingly.

Create a situation the user can run into and check which error code you get, then offer a list of possible causes to the user to act on.

Not strait forward but at least the user will have the possibility to correct the problem.
---
Stefan

Proud member of the UltraDefrag Development Team

CommonTater

  • Guest
Re: Returned error values....
« Reply #7 on: March 30, 2012, 04:19:16 PM »
I think you will have to use trial and error to be able to act on an error accordingly.

Create a situation the user can run into and check which error code you get, then offer a list of possible causes to the user to act on.

Not strait forward but at least the user will have the possibility to correct the problem.

Going at this experimentally makes my skin crawl  ...  as arrdvajk has pointed out, the codes could change or may already be different between versions of the OS.   ::)   
 
There are several spots where getting an unexpected error return could have disastrous results for the end user's data, which is why such elevated caution is needed.  Perhaps the best strategy in this case is to simply err well on the side of caution: If it's not the expected error value, stop what you're doing and shut it down.

It makes the code appear less intelligent but in this case it's probably a smart trade off...
 
Microsoft really should document a fixed set of values from the GetLastError() (or similar) function.  Things like "ERROR_READ" and "ERROR_WRITE" or "ERROR_DEVICE_NOT_READY" etc. as fixed and documented values could be a real benefit to those who are messing deeply with customer data.
 


EDIT:  I see I could have worded this last paragraph better...
 
I do know about winerr.h (etc.).  What I meant was they should produce a core set of system errors that are fixed and well documented; required to be produced by drivers in failure mode.  As aardvajk points out, it is currently possible (likely?) that different drivers will produce different error values for the same problems... which is not entirely helpful. 
 
« Last Edit: March 31, 2012, 06:17:00 PM by CommonTater »

CommonTater

  • Guest
Re: Returned error values....
« Reply #8 on: March 31, 2012, 10:16:44 PM »
Here's a little trick I found, that may be helpful to others with this same issue...

If you have the program return the value from GetLastError() when it exits, the IDE's console window will display it for you, so you can easily see if you're getting the expected results.  Here's the error printing function from the project I'm working on, as an example...  A screen shot is attached...

Code: [Select]
// display an error message
void ShowError(PWCHAR Fmt,PWCHAR Ins)
  { fwprintf(stderr,L"\nERROR : ");
    fwprintf(stderr,Fmt,Ins);
    fwprintf(stderr,L"\n\n");
    exit(GetLastError()); }

Already found some "odd balls" by experimentation...
Thanks for all the advice guys.... 
« Last Edit: March 31, 2012, 10:19:56 PM by CommonTater »

aardvajk

  • Guest
Re: Returned error values....
« Reply #9 on: April 01, 2012, 07:29:28 PM »
If you're just trying to make sure something either happens completely or not at all, try the transaction functions (CreateFileTransacted et al) since its what they're for.

Note that your ShowError function returns the error value after it has possibly been modified by WriteConsole/WriteFile or whatever Windows API fprintf uses to produce console output. You have no guarantee these don't change the error value on their success paths, only that the error value makes sense if they fail.

CommonTater

  • Guest
Re: Returned error values....
« Reply #10 on: April 01, 2012, 10:11:28 PM »
If you're just trying to make sure something either happens completely or not at all, try the transaction functions (CreateFileTransacted et al) since its what they're for.

Note that your ShowError function returns the error value after it has possibly been modified by WriteConsole/WriteFile or whatever Windows API fprintf uses to produce console output. You have no guarantee these don't change the error value on their success paths, only that the error value makes sense if they fail.

Actually I did take steps to verify that fprintf() doesn't write to GetLastError() ... interrestingly it seems to respect the stderr handle.   If I intentionally make a bad format string or send it a NULL pointer, it will write to GetLastError(), but not when it succeeds.  So far that's been reliable and at that point in the code I would not dream of making a decision on the value... it's just reporting it.

An example of  what I'm looking for is is in the screen shot...  When a path isn't found because the path doesn't exist it returns 2 (ERROR_FILE_NOT_FOUND) over a range of different functions (PathFileExists(), PathIsDirectory(), GetFileAttributesEx(), etc.) which in my case means the trap worked as expected.  For almost everything else (badly formatted paths, network authorization, etc.) it returns a range of different values... all of which are treated as a bail now signal. 
 
For Example:
Code: [Select]
       // target is current directory
      if (dstpath[0] == 0)
        dstpath[0] = L'.';
      // fully resolve target path
      { WCHAR tmp[MAX_PATH] = {0};
        GetFullPathName(dstpath,MAX_PATH,tmp,NULL);
        lstrcpy(dstpath,tmp); }
   
      // ensure target is reachable
      if (!PathIsDirectory(dstpath))
        ShowError(L"Target path is unreachable");
However, there are spots in the code where what comes back is very important to what happens next...
 
For Example:
Code: [Select]
// test for orphans
int IsOrphaned(PWCHAR SrcPath,PWCHAR File)
  { WCHAR stmp[MAX_PATH];

     PathCombine(stmp,SrcPath,File);
     if (PathFileExists(stmp))
       return 0;  // not orphaned
     
     if (GetLastError() == ERROR_FILE_NOT_FOUND)
       return 1;  // orphaned
 
     // can't trust result
     ShowError(L"Error tracking orphaned files"); }
     
There are spots like this in the project where I seem to have little choice but to rely upon the value from GetLastError() to make sure it's a trustworthy result... that is, in this case, I end up treating ERROR_FILE_NOT_FOUND as a success signal.

I know this isn't the best programming practice but unless someone has a better way of telling me if a file exists or not, without risk of a false negative, I'm kinda stuck with it.  I've tried FindFirstFile(), GetFileAttributes() and others and pretty much all of them have to be double checked in the same way. 
 
« Last Edit: April 01, 2012, 10:14:39 PM by CommonTater »

CommonTater

  • Guest
Re: Returned error values....
« Reply #11 on: April 04, 2012, 05:38:49 PM »
For those who may still have an interest in this thread, here is a copy of the first pass at the project I've been talking about. 

Be Warned
This is a very early version of the code and there may be issues or problems. 
Do not even think about using this program on live data!

Basically the task was to write an "open backup" program that would create mirrors of live data on external hard disks.  The idea was to make an exact copy of the tree structure on a second disk then update it periodically so he had an exact duplicate of his live data.  He wanted it in open format because he wanted to be able to drag and drop files out of the backup into his live data.  But he also wanted a feature most open backup programs (like X-Copy and it's cousins) do not have: Orphan control; the ability to remove files from the backup that do not exist in his live data.
 
When I informed him of the dangers of automated orphan control (for example: Deleting entire sub-trees which may have needed data in sub-folders) he decided against following through on it. 

So, I present the first version here as an example of the problems with Windows API calls not returning reliable and well documented error codes to developers...

Once again... be warned, this is "first blush" code... don't use it on live data!  If you want to test the program make copies of live data and work on the copies.


Offline Stefan Pendl

  • Global Moderator
  • Member
  • *****
  • Posts: 582
    • Homepage
Re: Returned error values....
« Reply #12 on: April 04, 2012, 06:19:22 PM »
Starting with Vista RoboCopy is included by default, so that would be the poor-mans solution compared to your application.
---
Stefan

Proud member of the UltraDefrag Development Team

CommonTater

  • Guest
Re: Returned error values....
« Reply #13 on: April 04, 2012, 07:14:26 PM »
Starting with Vista RoboCopy is included by default, so that would be the poor-mans solution compared to your application.

 :D Well... you learn something new every day... 
Buddy was an XP user, but still the danger of messing with orphans remains.
 
I'll mention robocopy to him and see what he thinks...  Another freebie from the tater!
 
Actually it's kind of good that I got into this.  I know a LOT more about windows error reporting now.

Thanks Stephan.   

 


Edit:
 
Try this...
Set up a sample data set with lots of pictures of various types in some kind of folder tree...
 
mirror32 -umr x:\source folder\*.gif y:\targetfolder
 
This will find all gif files in the source folder's tree and put them into a single targetfolder...
(Something RoboCopy can't do  ???  )
 
I tried this last night while testing and was able to write every .jpg from my entire data drive to a flash disk...  mirror64 -umr d:\*.jpg k:\ 
 

 
« Last Edit: April 04, 2012, 07:26:55 PM by CommonTater »