Well it has been a long time since I have posted. Whidbey has been taking up a lot of time lately, so unfortunately I have been unable to find the time to post.

Anyway, today I wanted to talk about ole32!GetErrorInfo /ole32!SetErrorInfo. These are the APIs that COM uses to communicate detailed error messages between components. Visual Studio uses these APIs for the same purpose, and hence I have had to deal with them.

First, off, I want to say that I do not like 'last error' style APIs. I have seen way to many cases where the last error is lost because of cleanup code. This is especially bad for error APIs because error APIs are only ever exercised in error case, which are usually quite rare. If you have to use last error APIs, then I would recommend setting the error in the entry point function but doing all of the work in a separate helper function. This way you never need to deal with a destructor clearing the error message that was just created.

Anyway, when I am debugging code that uses these APIs, there are two things that I want to know:

  1. What is the last error right now?
  2. How do I set a breakpoint when the last error changes?

Answers for Win XP SP2:

  1. Last Error = (IErrorInfo*)*(DWORD*)(272 + *(DWORD*)(@tib+3968))
  2. Evaluate '272 + *(DWORD*)(@tib+3968),x'. This will give you the address where COM stores the last error for the current thread. Set a data breakpoint on the address.

The two constants in these expressions (272 and 3968) are likely to change depending on OS version, so I will tell you how I figured this out. 

Hint: On Server 2003, change '272' to '280' and the expressions will work again.

Taking the answer apart:

So the first thing to note is '@tib'. @tib is a debugger pseudo-register that will tell you the memory address where the current thread's information block is located. The TIB, or the tib's bigger cousin the TEB (thread environment block) is how the operating system maintains all thread specific state such as exception handling functions, thread local storage (TLS) variables, etc. Here is the definition of the TEB that I got from winternl.h:

typedef struct _TEB {

    BYTE Reserved1[1952];

    PVOID Reserved2[412];

    PVOID TlsSlots[64];

    BYTE Reserved3[8];

    PVOID Reserved4[26];

    PVOID ReservedForOle;  // Windows 2000 only

    PVOID Reserved5[4];

    PVOID TlsExpansionSlots;

} TEB;

The interesting field in this structure is the 'ReserverdForOle' pointer. This points to all the OLE data (including the last error). So '3968' is the offset of the 'ReservedForOle' pointer in the TEB. You can figure this out by evaluating: &((_TEB*)0)->ReservedForOle.

The other constant is the offset of where OLE stores the error info in the OLE data. To determine this constant, I just made myself a simple app that does:

      CComPtr<CMyError> pError;

      pError.Attach(new CMyError());

 

      SetErrorInfo(NULL, pError);

And I ran it under the debugger. I dropped the address of the OLE data in the memory window, and saw where OLE stuck the value of 'pError'. It turns out that on my computer, ole stores the last error 272 bytes past the beginning of the structure.

One last note: you might ask yourself, why not just set a breakpoint on {,,ole32}_SetErrorInfo@8? The reason is that COM will internally update this error whenever you make a cross thread call. These updates don't go through the SetErrorInfo API.

Have a good Thanksgiving.