Every few months I heard people asking the same question:
Given a process ID (or handle), how can I get its main thread ID (or handle)?
Normally that would raise another question:
What is the definition of a main thread?
While the Windows operating system doesn't have a concept called main thread, and threads donnot have parent-child relationship at all.
Let's reuse the sample code from Pop Quiz - Debug Event Loop and Timeslice Quota:
If we compiled the code using cl.exe test.cpp, the generated test.exe would return immediately after we run it. If we take a quick debug, the call stack would look like this:
That's because the compiler has made the decision that we need to use CRT initialization, although actually we are not using it either explicitly or implicitly. It is the CRT exit code which called ntdll!RtlExitUserProcess and terminated our worker threads, and this is by design.
Now let's switch to the following command:
cl test.cpp /link /NODEFAULTLIB /ENTRY:WinMain /SUBSYSTEM:CONSOLE kernel32.lib
As you can see, test.exe would enter an endless loop.
Now let's try to give some possible definitions of main thread:
It looks like option 4 and 5 have the most clean definition. If we use option 4 then we should stay with the facts that a process may not have a main thread, and that's why we would normally end up with option 5.
Which one do you prefer and what is your own definition? Which option do you think the Visual Studio Debugger would use?
I would prefer some combination of these. For example, in GUI application I would refuse 5, if there is just one thread running the message loop and it is different from 5. You can have multiple threads with message queue and each servicing its own main window (or Frame window style). In that case the oldest would win.