Naming threads in Win32 And .Net

When you are debugging an application with multiple threads it can be handy to have a better name than just the thread id.  This is simple to do in managed code.  There is a property on the Thread object that you can set.  It is also possible to do this for native code. However, there is simply no way you will ever discover it unless someone points you to the difficult to find docs.  I knew about the feature and still missed the docs the first time I looked for them!

 

For completness, here is the managed version: Setting a Thread Name (Managed).

The more interesting one is the native version: Setting a Thread Name (Unmanaged) (There is a typo in the documentation's code; 'except' should be '__except'. I've included a corrected copy below.)

 

The native method of setting the thread name is implemented by raising an SEH exception that is continued.  If you go to the docs on RaiseException you'll see part of the reason for this strange mechanism.  An attached native debugger will get a 'first chance' notification of the exception.  Raising an exception is precisely what you need to do to get the native debugger's attention. The one raised here (0x406D1388) is recognized by VS (and WinDbg).

 

Note: If anything in your process installs a vectored exception handler, that handler will not be called on this exception code when the VS debugger is attached.  The VS debugger always handles this exception code and continues execution, preventing any further handlers.  In WinDbg you can go to Debug/Event Filters and set the "Visual C++ exception" to be disabled.  With that setting, your vectored exception handler will get a shot at 0x406D1388 while WinDbg is debugging it.

 //
// Usage: SetThreadName (-1, "MainThread");
//
typedef struct tagTHREADNAME_INFO
{
   DWORD dwType; // must be 0x1000
   LPCSTR szName; // pointer to name (in user addr space)
   DWORD dwThreadID; // thread ID (-1=caller thread)
   DWORD dwFlags; // reserved for future use, must be zero
} THREADNAME_INFO;

void SetThreadName( DWORD dwThreadID, LPCSTR szThreadName)
{
   THREADNAME_INFO info;
   info.dwType = 0x1000;
   info.szName = szThreadName;
   info.dwThreadID = dwThreadID;
   info.dwFlags = 0;

   __try
   {
      RaiseException( 0x406D1388, 0, sizeof(info)/sizeof(DWORD), (DWORD*)&info );
   }
   __except(EXCEPTION_CONTINUE_EXECUTION)
   {
   }
}