How to get the main thread ID of a process (known by its ID)?

13,402

Solution 1

#ifndef MAKEULONGLONG
#define MAKEULONGLONG(ldw, hdw) ((ULONGLONG(hdw) << 32) | ((ldw) & 0xFFFFFFFF))
#endif

#ifndef MAXULONGLONG
#define MAXULONGLONG ((ULONGLONG)~((ULONGLONG)0))
#endif

bool CloseProcessMainThread(DWORD dwProcID)
{
  DWORD dwMainThreadID = 0;
  ULONGLONG ullMinCreateTime = MAXULONGLONG;

  HANDLE hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
  if (hThreadSnap != INVALID_HANDLE_VALUE) {
    THREADENTRY32 th32;
    th32.dwSize = sizeof(THREADENTRY32);
    BOOL bOK = TRUE;
    for (bOK = Thread32First(hThreadSnap, &th32); bOK;
         bOK = Thread32Next(hThreadSnap, &th32)) {
      if (th32.th32OwnerProcessID == dwProcID) {
        HANDLE hThread = OpenThread(THREAD_QUERY_INFORMATION,
                                    TRUE, th32.th32ThreadID);
        if (hThread) {
          FILETIME afTimes[4] = {0};
          if (GetThreadTimes(hThread,
                             &afTimes[0], &afTimes[1], &afTimes[2], &afTimes[3])) {
            ULONGLONG ullTest = MAKEULONGLONG(afTimes[0].dwLowDateTime,
                                              afTimes[0].dwHighDateTime);
            if (ullTest && ullTest < ullMinCreateTime) {
              ullMinCreateTime = ullTest;
              dwMainThreadID = th32.th32ThreadID; // let it be main... :)
            }
          }
          CloseHandle(hThread);
        }
      }
    }
#ifndef UNDER_CE
    CloseHandle(hThreadSnap);
#else
    CloseToolhelp32Snapshot(hThreadSnap);
#endif
  }

  if (dwMainThreadID) {
    PostThreadMessage(dwMainThreadID, WM_QUIT, 0, 0); // close your eyes...
  }

  return (0 != dwMainThreadID);
}

Solution 2

A much simpler and surer way to get the thread id of the main thread is to let the main thread records its own thread id using ::GetCurrentThreadId() into a shared global variable, perhaps in your WinMain or somewhere at the very beginning of your 'main thread':

MainThreadId_G = ::GetCurrentThreadId();

then in your other threads, you can call: ::PostThreadMessage(MainThreadId_G, WM_QUIT, returncode, 0);

Solution 3

I've checked how this is handled in MFC, and it looks like UI thread is determined from constructor:

C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\atlmfc\src\mfc\appcore.cpp:

CWinApp::CWinApp(LPCTSTR lpszAppName)
{
...

    m_nThreadID = ::GetCurrentThreadId();

And using MFC call AfxGetApp()->m_nThreadID you can figure out UI thread ID.

However - this approach does not work if .dll was loaded not from main thread - then even MFC's approach will not work - AfxGetApp()->m_nThreadID will return something else than main thread.

But typically your .dll gets loaded from main thread, but your .dll is not necessary mfc enabled. I've could recommend approach like this:

class GetMainThread
{
public:
    GetMainThread()
    {
        m_nThreadID = ::GetCurrentThreadId();
    }

    DWORD m_nThreadID;
}getMainThread;


DWORD getUIThread()
{
    DWORD id = 0;

    if( AfxGetApp() != NULL )
        id = AfxGetApp()->m_nThreadID;
    else 
        id = getMainThread.m_nThreadID;

    return id;
} //getUIThread

If .dll is loaded by main UI thread, you will get correct thread id from constructor call (GetMainThread class).

Remove AfxGetApp() calls if you don't need them (In my application I needed those)

Solution 4

use this at the beginning of your cpp file, not in a function:

DWORD mainThreadID = ::GetCurrentThreadId();

This will initialize mainThreadID before your main function is being executed which guarantees to be the main thread.

Share:
13,402
Smehrt Tonni
Author by

Smehrt Tonni

BY DAY: A coder. BY NIGHT: A programmer.

Updated on June 25, 2022

Comments

  • Smehrt Tonni
    Smehrt Tonni almost 2 years

    Can you help me to find the main (only) thread ID of a given by ID process, please ?

    Task context: A running process has (at the moment) no windows but a(some) thread(s).

    Wanted: Posting of WM_QUIT at the main thread only.

    Not-wanted: Using of TerminateProcess or posting WM_QUIT at the non-primary threads.

  • Cody Gray
    Cody Gray about 11 years
    This answer would be better if it contained some explanation about what the code does and how it works. In particular, how are you determining what the "main" thread is of a particular process in the case where that process has multiple threads? It looks to me like you're basing it on the creation time of the thread. That's not necessarily a reliable heuristic; it's important to point that out where people can consider carefully the advantages and disadvantages of this approach. Answers that contain only code are not answers.
  • Harry Johnston
    Harry Johnston about 11 years
    Actually, there can be multiple message queues, each associated with a different thread, and if there are, you can use PostThreadMessage to send a message to a specific thread.
  • alex
    alex about 11 years
    @Harry: that's interesting to know! What is the realistic use case, when multiple queues would be present?
  • Harry Johnston
    Harry Johnston about 11 years
    I believe that if you call MessageBox from a thread, without specifying a parent window, the associated window messages are processed from that thread. You might also use message queues for internal messaging between threads, though personally I tend to prefer APCs.
  • Mordachai
    Mordachai almost 11 years
    -1 due to false/misleading "can't post to a specific thread" - yes, you can - q.v. PostThreadMessage. Actually, this would be much easier if Win32 offered a PostProcessMessage and your assumption was correct (or at least it worked this way for this purpose). But alas, not so...
  • Mark Ch
    Mark Ch over 8 years
    I definitely prefer this answer. Concise, provides a complete answer, and a good explanation. Of course, it is possible to pass the main thread ID in as a parameter to the worker-threads when they are created, instead of using a global variable.
  • mysticcoder
    mysticcoder over 5 years
    @MarkCh except that the creator threads may themselves be worker threads or some other job task etc. that may not have been themselves the main thread id. Not to mention all the bookkeeping and overhead instead of just having a global. Of course, a global may not be visible to static / dynamic link libraries adding further complexity.
  • Jedao
    Jedao almost 2 years
    You should also reset th32.dwSize before each call to Thread32Next, as it may modify it. And you need to check that the th32OwnerProcessID field is actually present. This is explained here: devblogs.microsoft.com/oldnewthing/20060223-14/?p=32173