Special preview content for my TechReady talk later today. I'd like to claim it was planned this way, but actually it was just a coincidence.

The Visual C++ compiler uses exception code 0xE06D7363 for C++ exceptions. Here's how you can decode the other parameters. (Handy if you're debugging a crash dump.)

Note that this information falls under the category of implementation detail. There is no guarantee that this method will continue to work in the future, so don't write code that relies on it. It's just a debugging tip.

When the C++ exception is raised, the exception code is 0xE06D7363 and there are three (possibly four) parameters.

  • Parameter 0 is some internal value not important to the discussion.
  • Parameter 1 is a pointer to the object being thrown (sort of).
  • Parameter 2 is a pointer to information that describes the object being thrown.
  • Parameter 3 is the HINSTANCE of the DLL that raised the exception. (Present only on 64-bit Windows.)

The object being thrown is pretty much the object being thrown, except that sometimes there is some junk in front that you have to skip over. Once you figure out what it is, you can dump it. (I haven't bothered trying to figure out exactly how much; I just dump bytes and figure out the correct start of the object by inspection.) But what is it? That's what Parameter 2 tells you, but in a very roundabout way.

Take Parameter 2 and go to the fourth DWORD and treat it as a pointer. (On 64-bit systems, you have to add this value to the HINSTANCE passed as Parameter 3 to convert it to a pointer.)

Next, go to the second DWORD and treat it as a pointer. (Again, on 64-bit systems, it's really an offset from the HINSTANCE.)

Next, go to the second DWORD and treat it as a pointer. (64-bit systems: you know the drill.)

Finally, skip over the first two void*s and the rest is the class name.

Here's a picture, rendered in high-tech ASCII line drawing. Pointer-sized fields are marked with an asterisk, and fields whose value are unknown or not important are marked with tildes.

EXCEPTION_RECORD
+----------+
| E06D7363 |
+----------+
|  ~~~     |
+----------+
|* ~~~     |
+----------+
|* ~~~     |
+----------+
| 3 or 4   |
+----------+
|* ~~~     |
+----------+
|*Object   |
+----------+     +---+
|*       ------> |~~~|
+----------+     +---+
|*HINSTANCE|     |~~~|
+----------+     +---+
                 |~~~|
                 +---+    +---+
                 | -----> |~~~|
                 +---+    +---+    +---+
                          | -----> |~~~|
                          +---+    +---+    +----------+
                                   | -----> |*   ~~~   |
                                   +---+    +----------+
                                            |*   ~~~   |
                                            +----------+
                                            |Class name|
                                            +----------+

"When in doubt, add another level of indirection" appears to be the mantra here.

Here's a real-world example I had to debug. This came from a crash dump in a third-party application reported via Windows Error Reporting, so all debugging has to be done without source code or symbols.

0:008> .exr 00000000`015dede0
ExceptionAddress: 000007fefd23bb5d (KERNEL32!RaiseException+0x39)
   ExceptionCode: e06d7363 (C++ EH exception)
  ExceptionFlags: 00000001
NumberParameters: 4 // this is running on 64-bit Windows
   Parameter[0]: 0000000019930520
   Parameter[1]: 00000000015def30 // object being thrown
   Parameter[2]: 00000000100cefa8 // magic Parameter 2
   Parameter[3]: 0000000010000000 // HINSTANCE

According to the cookbook, we follow Parameter 2:

0:008> dd 00000000100cefa8 l4
00000000`100cefa8  00000000 00000000 00000000 000cefc8
                                              ^^^^^^^^

and take the fourth DWORD. Since this is a 64-bit machine, we add it to the HINSTANCE before dumping. (If this were a 32-bit machine, we would just dump it directly.)

0:008> dd 100cefc8 l2
00000000`100cefc8  00000005 000ceff8
                            ^^^^^^^^

Now we take the second DWORD (add the HINSTANCE since this is a 64-bit machine) and then dump it again:

0:008> dd 100ceff8 l2
00000000`100ceff8  00000001 000d6670
                            ^^^^^^^^

Okay, we're within striking distance now. Since this is a 64-bit machine, we add the HINSTANCE to the offset. And on all platforms, we add two pointers (which is 0x10 on a 64-bit machine and 8 on a 32-bit machine). The result should be an ASCII string representing the class name:

0:008> da 100d6670+10
00000000`100d6680  ".PEAVCResourceException@@"

If you ignore the decorations, you see that this is telling you that the object thrown was a CResource­Exception.

And for old time's sake, here's a 32-bit version I just made up now.

0:000> .exr 0008f2e4
ExceptionAddress: 7671b046 (kernel32!RaiseException)
   ExceptionCode: e06d7363 (C++ EH exception)
  ExceptionFlags: 00000001
NumberParameters: 3 // 32-bit platform
   Parameter[0]: 19930520
   Parameter[1]: 0008f384 // object being thrown
   Parameter[2]: 10cfed60 // magic Parameter 2
0:000> dd 10cfed60 l4
10cfed60  00000000 00000000 00000000 10db297c
0:000> dd 10db297c l2
10db297c  00000004 10db2990
0:000> dd 10db2990 l2
10db2990  00000001 10dbccac
0:000> da 10dbccac+8
10dbccb4  ".PAVCFileException@@"

Anyway, back to the original problem: Knowing that the object being thrown was a CResource­Exception was a big help, because that's a class used by MFC, so I have additional information as to what it does and how it's used. This turns out to have been the necessary foothold to identify the source of the problem, which will be the subject of a future write-up.