Search This Blog

Wednesday 12 August 2015

WinDbg : Getting The Last Error in WinDbg

WinDbg : Getting The Last Error


One of the most used Windows APIs is perhaps GetLastError(). It returns us the error code of the last error encountered. However, many a times, we do not query for the last error while writing the code, and while debugging later on, we have no idea why it failed, we just know that it did. Here is an example:

HANDLE hVol = INVALID_HANDLE_VALUE;
hVol = CreateFile( csDrvName,
                           0,
                           FILE_SHARE_READ | FILE_SHARE_WRITE,
                           NULL,
                           OPEN_EXISTING,
                           0,                           // FILE_FLAG_NO_BUFFERING | FILE_FLAG_RANDOM_ACCESS
                           NULL);

if( hVol == INVALID_HANDLE_VALUE )
{
        return E_FAIL;
}

Now this function never queried the last error and if it had logging, then it never printed it to the logs. 

Which brings us to the question, if we had called GetLastError(), where does it get the info from, and if it did, then can we also get the same information from the debugger in cases when we wrote code like the above. The answer is Yes, we can, and here is how... 

Commands Used:
  1. !teb
  2. !error
Before we proceed, all error codes are thread specific, so being in the context of the thread you are debugging is important. To know know more about changing thread contexts this link might be helpful.  

Note : The fields of !teb sometimes vary between operating system versions. So if your output is slightly different from mine, there is no need to be concerned.

0:002> !teb
TEB at 7efd7000
    ExceptionList:        00b8fdf0
    StackBase:            00b90000
    StackLimit:           00b8e000
    SubSystemTib:         00000000
    FiberData:            00001e00
    ArbitraryUserPointer: 00000000
    Self:                 7efd7000
    EnvironmentPointer:   00000000
    ClientId:             0000099c . 00001d48
    RpcHandle:            00000000
    Tls Storage:          7efd702c
    PEB Address:          7efde000
    LastErrorValue:       2
    LastStatusValue:      0
    Count Owned Locks:    0
    HardErrorMode:        0


The value in LastErrorValue field is what GetLastError reports to us. It lives in the thread level, and that is why we can be multi threaded, and each thread can be calling a different API and the error codes don't get mangled.

Coming to the interpretation part. The !error command can interpret all errors and status messages in Windows.

0:001> !error 0n2
Error code: (Win32) 0x2 (2) - The system cannot find the file specified.

Note : Please be careful to use the right numeric base for !error. That is, if the value shown in !teb is in decimal, we should append a 0n to ensure it is not interpreted as hexadecimal.

Similarly, the LastStatusValue field will have the NTSTATUS codes.