What is the effect of memory-mapped file access on GetLastError()?

A customer was using memory-mapped files and was looking for
information as to whether access to the memory-mapped data
modifies the value returned by
Get­Last­Error.
A member of the kernel team replied,
"No, memory-mapped I/O does not ever change the value returned by
Get­Last­Error."

While it's true that the kernel does not ever change the value
returned by
Get­Last­Error,
it's also the case that
you might change it.

If you set up an exception handler, then your
exception handler might perform operations that affect
the last-error code, and those changes will be visible
after the exception handler returns.
(This applies to all exception handlers and filters,
not just ones related to memory-mapped files.)

If you intend to return
EXCEPTION_CONTINUE_EXECUTION
because you handled the exception,
then you probably should make sure to leave the last-error
code the way you found it.
Otherwise, the code that you interrupted and then resumed will have had its
last-error code changed asynchronously.
You just
sabotaged it from above.

If the Is­An­Exception­I­Can­Repair function
or
Repair­Exception function
does anything that affects the last-error code,
then when the exception filter is executed for a repairable
exception, the last-error code is magically changed without the
mainline code's knowledge.
All the mainline code did was execute stuff normally, and somehow
during a memory access or a floating point operation or some other
seemingly-harmless action, the last-error code spontaneously changed!

If you are going to continue execution at the point the exception was
raised, then you need to "put things back the way you found them"
(except of course for the part where you repair the exception itself).

Am I being simple if I say that you should just save the last error immediately upon encountering an error if you need it?

[This assumes you get a chance! Consider: m_hEvent = CreateEvent(...); if (!m_hThing) { GetLastError(); }. You might take an exception trying to write to m_hEvent, or even just trying to fetch the next instruction from memory. Both of these happen before you get a chance to call GetLastError. Remember, these are asynchronous exceptions, not C++ exceptions. -Raymond]

@Joshua: By not using the same name with different capitalization for the class and the instance. That's just confusing and makes the code harder to read. That's one of the reasons why I prefer case insensitive languages. No case sensitivity = no case sensitivity abuse.

It's not an abuse. It's a common idiom in case sensitive languages that types have an initial capital letter and variables do not. Thus, if one understands the culture of the language in use, there is no confusion. And by using the same name, the type becomes very easy to infer.

I've seen this recommended in both Java and Objective-C (languages I use daily).

This isn't really a matter of case sensitivity. Keep in mind that the following code would function exactly the same way:

PreserveLastError PreserveLastError;

I feel that the real issue is whether or not to use a class designed in this way. I feel that it's not intuitive to declare a variable (apparently unused) in order to preserve the error code. It is, however, quite practical.

@Mason Wheeler: I'd agree with you if you were editing your code in 1990. But this is 2012, every code editor has color-coding, and there's no confusion involved because type names have a different color from variable names.

If you're not using color-coding, then I'd say the problem exists between keyboard and chair.

@James I don't even see the problem in 1990. Classes start with an uppercase letter, variables with a lowercase one. If you're not using stupid fonts where l and I are indistinguishable and other fun things, it's perfectly clear what what is in every possible situation at one glance.

I don't like using variables like this preserveLastError whose only purpose is to effect a change on construction and destruction — some ignorant programmer could just come along and say "oh hey, it looks like this variable isn't being used for anything, I'll just delete it", and suddenly your program breaks in impossible ways two months later.

Yes, the class and variable are well-named in this case, and you should avoid trying to work with obtuse programmers as much as possible, but it's still a possibility to worry about. I'm particularly fond of the "explicit is better than implicit" rule from the Zen of Python.

@Adam Rosenfield: The problem is, C++ doesn't let you do this explicitly. You could add an explicit GetLastError() to the beginning and a RestoreLastError() before every return, but then you aren't exception-safe: any exception will prevent the code from restoring the error code.

The exception-safe explicit way would be a try-finally construct, but C++ doesn't support that. All you can do is writing a class for every possible cleanup scenario, and declare an instance of that class any time you need that specific cleanup code.

Won't your printf() call potentially change the value of GetLastError() too? That's not a signal safe function on Unix (where it can do bad things like acquire C runtime library mutexes that could already be held), so I'm suprised to see it used in a Windows exception handler.

@Evan: Well, I wouldn't call what ~PreserveLastError() does in this example "resource cleanup". I guess that's why this solution bothers me — if the destructor would truly just clean up resources acquired in the constructor, no one would be surprised. I believe that the Java equivalent:

int lastError = GetLastError();

try {

// do stuff

} finally {

RestoreLastError(lastError);

}

is both more explicit and more readable than the C++ code:

{

PreserveLastError preserveLastError;

// do stuff

} // Note: the last error is magically restored here

Maybe the Java version is prone to copy-pasting (as you didn't abstract your setup and restoration into separate methods), but at least you can clearly see that something is happening at the end of the block.

Man, a lot of very strong opinions on such a trivial detail of the post!

I still find it hard to keep in mind that not every SEH exception is an error. Though I expect that people writing SEH vectored exception handlers would be better about keeping an eye on this sort of thing?

PreserveLastError is a proper use of the RAII idiom, though I agree that an explanatory comment would be worthwhile. I really like D's scope(exit) construct; thankfully, with C++11, you can recreate its semantics. The following code works in VC10 and VC11:

> I don't like using variables like this preserveLastError whose only purpose is to effect a change on construction and destruction — some ignorant programmer could just come along and say "oh hey, it looks like this variable isn't being used for anything, I'll just delete it", and suddenly your program breaks in impossible ways two months later.

I'll confess that my initial thought once saw it is that I can't see any read or write action to it, so it should be removed.

It would be better if some comments can be put beside the code, I think.

[Um, the comments are in the accompanying prose. You forget that code is formatted differently for the Web site. -Raymond]

Further, even *if* C++ had try-finally, using that for resource cleanup is, at least I think, super obnoxious. Deterministic destructors are way better. The only solutions that I consider comparable are something like Python's 'with' or C#'s 'using' and D's 'scope(exit)'.

@Deduplicator – For most developers, their code code does not cease to be used once it is complied. As someone who spends a lot of time reading other people's code, often written in languages in which I do not regularly write myself, you'd be hard pressed to over-comment code. Making assumptions about the depth of familiarity of your reader with the the language is a mistake.

Comments are a beautiful thing to accelerate others understanding of the INTENT fo your code. Since developers are not perfect, their code sometimes deviates fronm their intent. Comments help ME to find YOUR mistakes.