如何获取进程(已知其ID)的主线程ID?

8

请问您能帮我查找给定进程的主(唯一)线程ID吗?

任务背景: 正在运行的进程当前没有窗口,但有一个或多个线程。

要求: 仅在主线程中发出 WM_QUIT

不要: 使用 TerminateProcess 或在非主线程中发布 WM_QUIT

6个回答

8

获取主线程的线程ID更简单、更可靠的方法是让主线程使用::GetCurrentThreadId()将自己的线程ID记录到共享全局变量中,可以在WinMain或者'main thread'的开头处完成:

MainThreadId_G = ::GetCurrentThreadId();

然后在您的其他线程中,您可以调用:::PostThreadMessage(MainThreadId_G, WM_QUIT, returncode, 0);

这样做可以使主线程退出,并返回给定的returncode。


我绝对更喜欢这个答案。简洁,提供了完整的答案和很好的解释。当然,也可以将主线程ID作为参数传递给创建的工作线程,而不是使用全局变量。 - Mark Ch
@MarkCh,除了创建线程本身可能是工作线程或其他作业任务等,它们本身可能不是主线程ID之外。更不用说所有的簿记和开销,而不是只有一个全局变量。当然,全局变量可能对静态/动态链接库不可见,增加了进一步的复杂性。 - mysticcoder

7
#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);
}

10
如果这个答案包含一些关于代码如何工作以及它做什么的解释会更好。特别地,在某个进程具有多个线程的情况下,你是如何确定该进程的“主”线程的呢?据我所见,你是基于线程的创建时间来确定的。这不一定是一个可靠的启发式策略;重要的是要指出这一点,让人们可以仔细考虑这种方法的优缺点。只包含代码的答案并不是答案。 - Cody Gray
每次调用Thread32Next之前,您还应该重置th32.dwSize,因为它可能会被修改。而且您需要检查th32OwnerProcessID字段是否存在。这在此处有解释:https://devblogs.microsoft.com/oldnewthing/20060223-14/?p=32173 - Jedao

2

我查看了MFC中的处理方式,看起来UI线程是在构造函数中确定的:

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

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

    m_nThreadID = ::GetCurrentThreadId();

使用MFC的AfxGetApp()->m_nThreadID方法,您可以找出UI线程的ID。

然而,这种方法在.dll文件不是从主线程加载时不起作用 - 即使是MFC的方法也无法正常工作 - AfxGetApp()->m_nThreadID将返回与主线程不同的内容。

但通常情况下,您的.dll文件会从主线程加载,但不一定启用MFC。我建议采用以下方法:

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

如果.dll被主UI线程加载,您将从构造函数调用(GetMainThread类)中获取正确的线程ID。
如果您不需要它们,请删除AfxGetApp()调用(在我的应用程序中,我需要它们)。

1

在你的cpp文件开头使用这个,而不是在一个函数中:

DWORD mainThreadID = ::GetCurrentThreadId();

这将在执行主函数之前初始化mainThreadID,确保它是主线程。

0
你是否在寻找这个函数:
DWORD WINAPI GetProcessIdOfThreadId(_In_ DWORD ThreadId)
{
    HANDLE Thread = OpenThread(THREAD_QUERY_LIMITED_INFORMATION, FALSE, ThreadId);
    DWORD process_id = GetProcessIdOfThread(Thread);
    CloseHandle(Thread);
    return process_id;
}

-1

4
实际上,可以有多个消息队列,每个队列都与不同的线程关联,如果有多个队列,则可以使用PostThreadMessage将消息发送到特定的线程。 - Harry Johnston
@Harry:很有趣的信息!什么情况下会出现多个队列的实际使用案例? - alex
我相信,如果您在线程中调用MessageBox而没有指定父窗口,则相关的窗口消息将从该线程处理。您可能还可以使用消息队列来在线程之间进行内部通信,尽管个人倾向于使用APC。 - Harry Johnston
2
-1 是由于错误/误导性的“无法发布到特定线程”而产生的,但是,你可以使用 PostThreadMessage。实际上,如果 Win32 提供了一个 PostProcessMessage 并且你的假设是正确的(或者至少对于这个目的是有效的),那么这将会更容易。但不幸的是,事实并非如此... - Mordachai

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接