Search code examples
windowsmultithreadingwinapiwin32-process

How to correctly determine a process' primary (first) thread


There have been many questions about how to identify the process' main thread. A common answer was that there is no "main" thread and all threads are equivalent.

This is an incorrect and highly theoretical answer. The main thread is the first one created by the process. The Visual Studio debugger in the Threads window can somehow distinguish the main thread from the others. And I want to know, at the time of loading the statically linked DLLs (before WinMain), how the debugger does it. Ultimately, I want to get the ID of this thread.

EDIT: As @IInspectable stated, "While there is a thread that ultimately initializes a process that thread doesn't have any distinguishing features. It can terminate at any point while other threads owned by the process continue executing". I know this and I completely agree with it. But I'm dealing with a giant 30-years-old product consisting of several interacting EXEs and several hundreds DLLs. And many different DLLs (which know nothing about each other and about EXEs) have pieces of code that are only allowed to run on the main thread. Otherwise the application crashes. You can repeat many times that this is a very bad design, but this is what I have and I can't do anything about it. And this is what I call "practice", as opposite to abstractly true but useless statements.

Therefore, our product needs a sort of service that tells whether the current thread is the process' primary thread. So I chose a very basic DLL that is statically linked to all EXEs and almost all other DLLs, and implemented such a "service" there exactly as described in @MSalters answer.

This worked well until recently I started receiving complaints from developers in another country's office, that the chosen DLL was being loaded on a worker thread (causing the application to crash). We cannot reproduce this phenomenon in our office, and so all I have are snapshots of the debugger's thread window. Below is the one of them; if it tells something to somebody, I would be very happy to know.

enter image description here


Solution

  • The debugger can show which thread (if any) originated from the EXE's entrypoint. This entrypoint is not WinMain, because your compiler needs the entrypoint to set up its runtime. That runtime calls your WinMain.

    At DLL loading time (i.e. DllMain) your code runs under Loader Lock, and there is very little you can do at this time. But the call to DLL_PROCESS_ATTACH specifically happens on this first thread. So you don't need to do anything to find out this thread. You can safely use TlsAlloc to mark this thread for future use. You can also safely call GetCurrentThreadId since this is a kernel32 function, which is already loaded before DllMain.