Enumerating threads in a process


The tool helper library is sort of the black sheep of Win32. It grew out of the 16-bit TOOLHELP library, which provided services for system debugging tools to do things like take stack traces and enumerate all the memory in the system. The original incarnation of Win32 didn't incorporate it; it wasn't until Windows 95 that a 32-bit version of the tool helper library sort of got bolted onto the side of Win32.

Disowned or not, the functions are still there, so let's give them a spin.

#include <stdio.h>
#include <windows.h>
#include <tlhelp32.h>

int __cdecl main(int argc, char **argv)
{
 HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
 if (h != INVALID_HANDLE_VALUE) {
  THREADENTRY32 te;
  te.dwSize = sizeof(te);
  if (Thread32First(h, &te)) {
   do {
     if (te.dwSize >= FIELD_OFFSET(THREADENTRY32, th32OwnerProcessID) +
                      sizeof(te.th32OwnerProcessID)) {
       printf("Process 0x%04x Thread 0x%04x\n",
             te.th32OwnerProcessID, te.th32ThreadID);
     }
   te.dwSize = sizeof(te);
   } while (Thread32Next(h, &te));
  }
  CloseHandle(h);
 }
 return 0;
}

Running this program prints a list of all the threads in the system (or at least all the ones you have access to). This is particularly straightforward, the only subtlety being the strange check that the size returned by the Thread32First function is large enough to emcompass the th32OwnerProcessID field that we need. This complexity is necessary due to the somewhat unorthodox way that the Thread32First and Thread32Next functions check structure sizes.

That's what happens when you're the black sheep of the Win32 API.

Comments (11)
  1. 8 says:

    "a 32-bit version of the tool helper library sort of got bolted onto the side of Win32"

    Aha! So THATS how Windows 95 was made…

  2. mithuns says:

    Are there any other libs that allow you to enumerate running threads? The PSAPI and WtsApi32 libs allow you to only enumerate processes but not threads……..

  3. Carlos says:

    @mithuns

    HKEY_PERFORMANCE_DATA can give you a list of processes and threads, and it’s been around since NT 3.1 where it was the only supported method of doing this.

    Unfortunately: it’s a chore to use; third-party performance providers can leak memory into your process, bombard it with messages, and break performance data altogether; the performance text registry key is some kind of hack backed by files in system32 which used to break regularly (I haven’t tried it recently); and it’s not supported on Win 9x.

    If you don’t need the performance data it’s probably not worth the pain.

  4. Mike says:

    This again reminds me that the most required functionality of Win32, is also the most hary.

    I first read about threads in an article about OS/2 back in… man, the dark ages. :-) I then got to know NT, and noticed it lacked even the basic stuff – the stuff even the Amiga OS had (my background when it comes to preemptive multitasking systems). One of the missing things were "I provide memory, you provide a snapshot of the system state I request". That is AFAIK the only reliable way in a dynamic system, without this anyway happening behind the scenes (which it doesn’t do in NT).

    Add then all the different ways you need to do this – all the gazillion ways from NT 3.51 to NT4, to Win95, to 98, to NT5 to… and every "new" "innovation" (or whatever they’re called) deprecates the previous way of doing it. Man, that’s a cesspool. I now see there’s no wonder I just gave up.

    This however brings me to what I consider a much, much worse problem with NT – a problem likely not anticipated and impossible to retrofit: Named threads. BeOS got this right; every thread also has a "name", a string provided when the thread was created. Why is this a problem in NT? Have you ever tried to debug a DirectX-using application, and having been forced to visit every bl**dy thread just to see what threads are "DInput", "DSound" and so on? Add the suggestion of having a basically "dead" thread just waiting for an object…

    Add to the mess that how threads actually work isn’t 100% documented (as that would require 100% documentation of APC’s – for a user-mode API).

    I know, I expanded beyond the subject. Sue me.

  5. M Hotchin says:

    For threads that you control, you can name them in the debugger by looking at this article:

    http://www.highprogrammer.com/alan/windev/visualstudio.html

    or here:

    http://msdn2.microsoft.com/en-us/library/xcb2z8hs.aspx

  6. junfeng says:

    Raymond,

    You should add a disclaimer, that tool helper library is not safe to use in product system. They read other processes’ memory without synchronization. As a result it may corrupt other processes’ memory.

  7. Purplet says:

    > They read other processes’ memory without synchronization.

    How can this corrupt other processes’ memory ?

    I can see how it can potentially read corrupted/invalid data, but not how it can corrupt other processes memory [other than faulting, but I hope the OS will check for these..]

  8. KJK::Hyperion says:

    junfeng, that’s not true. The processes and threads list is returned atomically by the kernel, and the module and heap walkers are based on RtlQueryProcessDebugInformation, which creates a paired remote thread in the process to examine and returns a coherent, read-only snapshot. PSAPI is the unsafe one

  9. KJK::Hyperion says:

    Oh, Raymond, you may want to use RTL_CONTAINS_FIELD instead of FIELD_OFFSET + sizeof

  10. junfeng says:

    I stand correct.

    Going kernel does not mean it is safe. Unless you freeze the process in question, reading the process information while the process is running is unsafe action.

    Creating remote thread is even more dangerous.

    Now I did not say that you should not use those APIs. The chance of causing problem is rare and the usefulness of the information is apparent. But you do need a disclaimer just in case.

Comments are closed.