7

This is going to be a really stupid question and I hope it's fine for Stack Exchange at all, let alone just this part of it...

Right now, my custom Table control uses panic() functions that print an error on screen and DebugBreak() when a Windows API error occurs.

This is obviously an issue, but I'm not completely sure how to go about fixing it without effectively ignoring errors entirely. Keyword "effectively": even if I catch errors, if I can't do anything with them (be it forward the error to the user or to something else) it doesn't really count if I check for them.

Now for user-initiated functions (like tableAddColumn in my control) this isn't really an issue: just return the error information as a return value to the function.

My question is: what's the best way to handle Windows API errors in response to things where I can't obviously just return error information somehow. For instance, what if one of the drawing functions that I call in response to WM_PAINT fails? None of the system-provided messages provide functionality to return the API error to the system; they all just tell you to return zero if you handled the message.

I was thinking of creating a WM_NOTIFY notification tableErrorOccurred that gets sent to the control's parent when an API error occurs. However, I'm not sure how that would interact with the accessibility code. Windows accessible code is all COM, so I can freely return API errors via the HRESULT returns. Should those count toward the notification, or not? (Especially since the notification can and will return more information than just the HRESULT.)

I'm not sure whatever options there are, other than discounting the possibility of an error when all error conditions are made impossible. My current policy is to only check for an error when a function explicitly says it returns an error and how (for example, SendMessage(), EndPaint(), and SetScrollInfo() don't), and to ignore any commentary on which errors can be returned, in case the list of possible errors for whatever reason changes in the future. Am I wrong in this particular scenario: that is, would the error returns be guaranteed to never change and thus functions that only return errors on invalid parameters guaranteed to never fail with known valid parameters?

Some people have told me or talked about another Raymond Chen adage, "don't check for errors you don't know how to handle". Maybe someone can tell me that I'm misinterpreting this statement or something to that extent? Especially after the second link: it asks about what you would do with the error. I know what I want to do with these API errors: send them up to the thing that issued my own API request, just like every other API request in (almost) every other API ever! Is there a difference between doing this and "handling" the error? (You can tell that as I write more and more of this question, I am getting less and less sure of what my own opinion is, or whether any of this even IS a problem...)

The most common cases are calling GetClientRect(), which I could just save in my data structure if I can guarantee there are no cases in which the return value can change without a WM_WINDOWPOSCHANGED, and getting the height of the currently selected text font, which I can't save without sacrificing DPI independence (I deliberately get it every time to avoid the risk of crossing a DPI change boundary should a user program enable DPI awareness). The files util.h and coord.h have more info, but scroll.h and draw.h have the most API calls as far as I know.

The bits of the wine source code that I've seen don't check for errors. That's the only bit of information I have to say at this point. I'm not going to doubt that wine is a quality piece of software with a lot of effort put into it, but it seems odd to me that it does work well without explicit error checks on every API call...

I really don't want to rant about things here, so apologies in advance if this paragraph (and the previous) doesn't seem to go anywhere: It would be easier if I could return errors up to the system, or from SendMessage() (with the LRESULT as an out parameter, like COM method calls do). I don't really know. My experience with other GUI toolkits is that in most cases the only errors are incorrect parameters, but that really seems suspiciously optimistic to me. My main programming language for non-GUI stuff right now is Go, where errors are so fundamental that they're handled throughout both the standard library and user libraries (and have always been this way, without changes over time), and thus we know to carry errors up through the call chain; this reflects my own idea of error handling (and vice versa, to some extent).

So if anyone has any advice from the real world, that would be great, especially as I am close to finishing this and splitting out from the github project linked into something general purpose. Thanks.

EDIT 1 March 2015
I don't know why I forgot to paste this earlier but this is what some people on IRC said the day I posted this

[Sunday, February 15, 2015] [06:09:44 PM] <Rena>        andlabs, I think by "don't check for errors you can't handle" they mean just ignore them entirely.
[Sunday, February 15, 2015] [06:09:59 PM] <Rena>        I mean I can't really think of a sane thing to do if WM_PAINT is failing
[Sunday, February 15, 2015] [06:10:39 PM] <Rena>        sometimes there's nothing you can do but let it break
[Sunday, February 15, 2015] [06:19:17 PM] <andlabs>     Rena: yes, and that just goes against /everyhting/ I've been taugh tand everyone has been teaching everytone else!
[Sunday, February 15, 2015] [06:19:23 PM] <andlabs>     that's why I'm [expletive] confused!
[Sunday, February 15, 2015] [06:20:54 PM] <andlabs>     Rena: point that out anyway :S
[Sunday, February 15, 2015] [06:21:09 PM] <devin>       nobody ever tries to catch std::bad_alloc in C++ because if you've even reached the point where that's being thrown under normal circumstances then you're probably [expletive] and should just let the ship sink anyway
[Sunday, February 15, 2015] [06:21:37 PM] <Rena>        well things like, what are you even going to do if drawing something fails? it's not a huge issue and there's probably nothing you can do about it.
[Sunday, February 15, 2015] [06:21:49 PM] <Rena>        probably it means the system is running out of GDI memory or something
[Sunday, February 15, 2015] [06:22:47 PM] <andlabs>     Rena: right, at least in the case of WM_PAINT
[Sunday, February 15, 2015] [06:23:09 PM] <Rena>        if it's like memory allocation failed in some tool, it'd be a good idea to handle that by throwing up an "out of memory" message and/or trying to save the current file to a scratch file somewhere, instead of just crashing and losing work
[Sunday, February 15, 2015] [06:23:43 PM] <andlabs>     right
[Sunday, February 15, 2015] [06:23:59 PM] <andlabs>     ...actually I'll have to keep a note of that
[Sunday, February 15, 2015] [06:24:23 PM] <andlabs>     the other situations in which a reallocation would fail is
[Sunday, February 15, 2015] [06:24:25 PM] <andlabs>     a) heap corruption
[Sunday, February 15, 2015] [06:24:30 PM] <andlabs>     in which case you actually ARE [expletive]
[Sunday, February 15, 2015] [06:24:40 PM] <andlabs>     b) passing a pointer to something that isn't part of the realloc heap
[Sunday, February 15, 2015] [06:24:51 PM] <andlabs>     c) passing an obviously bad size like -4
[Sunday, February 15, 2015] [06:38:02 PM] <andlabs>     Rena, devin - yeah :/
[Sunday, February 15, 2015] [06:38:07 PM] <andlabs>     I figure that much :S

This comment on a pseudo-related question I posted on Stack Overflow (about a specific scenario) explains my state of mind as well.

1

Whenever an error happens one of the following scenarios must be true:

  1. You have checked for that error
  2. You have not checked for that error

In case of #2 your application will crash and it's your job to make sure it crashes gracefully (nice message to user, logs and so on). In order to do that you need to check for errors which means #2 is something you should never do.

All errors must be checked. No errors should be ignored.

Now there could be different interpretations of the term 'handle' when people say 'you should handle the error'. You can interpret as:

  • Check for errors
  • Do 'something' with them

This 'something' can be:

  • Recover so the application continues running
  • Fail gracefully.

When you decide to fail you have also to decide if the application will continue so the user can simply acknowledge the error and retry (maybe) or you decided the error is too serious and you better force the user to quit and start over.

You could intentionally decide to ignore errors as long as you have some sort of 'global-catch-all-errors' mechanism. If you don't then it's your job to check for every possible error and do something about it. Even if this something means just showing a message writing a log and quitting.

  • If there's nothing to be done for an error, checking for it to ignore it is useless. Also, sometimes errors are harmless and can be simply ignored completely. If those errors are signaled with exceptions, the empty catch-block should contain a good comment. – Deduplicator Apr 5 '15 at 20:42
  • @Deduplicator you're right. There are few scenarios were errors can be ignored, however as a general rule it's better to say they can never be ignored. Exceptions to the rule do not change the rule, they're just exceptions. If you think about it there are very few scenarios where you actually have a good reason to ignore an error. – Alex Apr 5 '15 at 20:55
  • @Deduplicator the harmless error are actually errors where you know what to do with them. – Pieter B May 6 '15 at 8:08
1

There are programming errors, and so-called error messages depending on something outside your application.

As an example: If my code tries to open a file and receives an error message, there wasn't really an error: The OS just informs me that for some reason the file could not be opened. Assuming that the OS was correct, this isn't an error, but one of the possible outcomes of trying to open a file. If my code is good, it will react to this error message properly.

Now another example: I create a file and write to it, I close it, and I reopen it. I expect that opening the file never fails. I have no code to handle that failure. In that case, if opening the file ever fails, that shows there is a programming error; I made a wrong assumption.

Windows Paint method fails: I was 100% sure it couldn't fail. So the failure is either a programming error in Paint () or my programming error (my code was based on an incorrect assumption). Anyway, it's a programming error.

With programming errors, you have two choices: Pray that they don't happen, or gracefully crash your program. With error messages, you have two choices: Detect them and handle them correctly, or turn them into "programming errors" which means you pray they don't happen, or you gracefully crash your program.

("Gracefully" means that the crash should not leave permanent damage, and should not lead to major loss of work. The latter assumes that work has been stored from time to time to recover after a crash).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.