0

Fun exploring some legacy code today. Ran into this little number:

function Func1()
{
DWORD dwError;
try
{
    dwError = 1;
    throw "Hey!";
} catch (LPCTSTR szError)
{
    Log("Log1: %d", dwError);
    SetLastError(dwError);
    throw szError;
}
}

function Func2()
{
    try {
       Func1();
    } 
    catch (LPCTSTR szError)
    {
         DWORD dwLastError = GetLastError();
         Log("Log2: %d", dwLastError); ///OMG is 0!
    }
}

GetLastError() returns 0! Why is that? The functions are actually a bit more complicated than this. They do include a few things on the stack (DWORDs, CString, BYTE[]). What should I be looking for?

Logs look like:

Log1: 1

Log2: 0

Adam Driscoll
  • 9,395
  • 9
  • 61
  • 104
  • If you call `SetLastError` to set the "last error" value of the current thread to zero it's obvious that, if no other function that modifies it is called, `GetLastError` returns zero... What is your question exactly? – Matteo Italia Sep 15 '11 at 21:30
  • SetLastError should be setting the last error to 1 not 0. – Adam Driscoll Sep 15 '11 at 21:33
  • `throw szError;` usually instead of throwing the exception recieved, one would simply use `throw;` – Mooing Duck Sep 15 '11 at 21:35
  • Uh, sorry, for some reason I missed the 1. :S Are you sure that no Windows function is called between the `throw` and the `catch`? Maybe into destructors of objects created on the stack... Otherwise, it may be that the CRT code that manages the exception uses some Windows function that sets the last error state. Anyway, if you want to pass around the state of `GetLastError` it's better to pick it up at the moment of throwing the exception and encapsulate it into the thrown exception. – Matteo Italia Sep 15 '11 at 21:36
  • @Mooing Duck That is a good recommendation. I will see if that fixes the issue. I'm still curious as to why GetLastError is being reset in this circumstance. – Adam Driscoll Sep 15 '11 at 21:36
  • @Matteo You are probably right about the CRT. I'll try Mooing's suggestion to see if it remedies it otherwise I will have to create a better exception struct to grab the message and the error code right away. – Adam Driscoll Sep 15 '11 at 21:39
  • @Adam: while the Mooing's tip is a good style tip, I don't think it should make a difference for your problem, since szError is just a pointer. – Matteo Italia Sep 15 '11 at 21:40
  • 1
    @Adam: my recommendation of `throw` shouldn't fix anything. It just prevents the "hidden" exception object from being re-created. For a pointer like you have, it makes no real difference. – Mooing Duck Sep 15 '11 at 21:46
  • Ok. Thanks for the advice. I'll keep looking into this weirdness. – Adam Driscoll Sep 15 '11 at 21:52

1 Answers1

2

C++ exceptions in the MSVC compiler and runtime are built on top of native Windows SEH. Stack unwinding is actually performed by Windows. Using Windows api functions is going to affect the value stored for GetLastError(). More details about the connection with SEH in this answer.

Community
  • 1
  • 1
Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • Would this not occur is I were to compile the project /EHs? – Adam Driscoll Sep 15 '11 at 22:35
  • It would, throwing exceptions and unwinding the stack is done by winapi functions under the hood, regardless of the compile options. – Hans Passant Sep 15 '11 at 23:11
  • Thanks Hans. I did discover something interesting though, which I should have included in the question. /clr is on. If I turn /clr off the GetLastError() behaves correctly. – Adam Driscoll Sep 15 '11 at 23:19
  • Well, lots and lots of more code runs when you throw exceptions in managed code. The CLR's exception handling is massively intricate. Kinda glad you mentioned that because I had a though time finding docs on how RaiseException and RtlUnwindEx() affect GetLastError :) – Hans Passant Sep 15 '11 at 23:32
  • I totally should have included that in the original post...duh... :P Thanks again. – Adam Driscoll Sep 15 '11 at 23:49