Raymond Chen posted about SetLastError recently, and an interesting discussion ensued. One comment in particular caught my eye:
The easiest way to catch a specific last error value in debugger is to set ntdll!g_dwLastErrorToBreakOn to that value.
A good while back I needed to break when such a LastError is set, and dug up all sorts of hacks to do so – breaking at SetLastError, setting data breakpoint on the thread-env-block-error, and the like. Beyond being cumbersome and plain ugly such breakpoint tricks can be very slow, and give a lot of false positives.
Seems the Win32 folks had similar needs and I was glad to discover they formed a better (undocumented, but still) solution. The authoritative source seems to be a 2007 post from Microsoft’s Daniel Pearson:
..Hiding inside of kernel32’s address space is a global variable called g_dwLastErrorToBreakOn. It turns out that SetLastError checks the value of this variable and if it’s non-zero, calls DbgBreakPoint if the two [values] match.
It’s a zero-overhead trick, and is very easy to do in Visual Studio: make sure kernel32.dll symbols are loaded, then type in a watch window –
(int*){,,kernel32.dll}_g_dwLastErrorToBreakOn
– and edit the referenced int:
Pearson notes two changes introduced in Vista:
(1) Up until XP, only Win32 API implemented in KERNEL32.DLL actually used SetLastError (and so tested g_dwLastErrorToBreakOn) – other dll’s used to set the error value via RtlSetLastWin32Error. Since Vista all Win32 API which set an error do so with SetLastError, so the g_dwLastErrorToBreakOn is much more reliable.
(2) Since Vista, g_dwLastErrorToBreakOn moved to NTDLL.DLL, so the VS usage should be changed to –
(int*){,,ntdll.dll}_g_dwLastErrorToBreakOn
It’s interesting to note that ntdll.dll does contain a separate instance of g_dwLastErrortToBreakOn also on XP machines:
But I verified that this value is never read, on calls into both kernel32 and ntdll.
Great post – will come in handy!
BTW, what is the default value? does it ignore (ie. not break) when zero?
Hey Niv!
The default is -1, and it is indeed ignored when breaking.
The post you quote is long gone, but can still be found via the Internet Archive: https://web.archive.org/web/20131031135425/http://blogs.msdn.com/b/danpear/archive/2007/04/06/2033100.aspx