关于如何识别进程的主线程,存在很多疑问。一个常见的答案是没有“主”线程,所有线程都是等效的。
这是一个不正确且高度理论化的答案。主线程是进程创建的第一个线程。 “线程”窗口中的 Visual Studio 调试器可以以某种方式将主线程与其他线程区分开来。我想知道,在加载静态链接的 DLL 时(在 WinMain 之前),调试器是如何执行的。最终,我想得到这个线程的ID。
编辑:正如@IInspectable所说,“虽然有一个线程最终初始化一个进程,但该线程没有任何显着特征。它可以在任何时候终止,而该进程拥有的其他线程继续执行”。我知道这一点并且完全同意。但我正在处理一个已有 30 年历史的巨大产品,由几个交互的 EXE 和数百个 DLL 组成。许多不同的 DLL(它们彼此之间以及 EXE 都一无所知)都有一些只允许在主线程上运行的代码。否则应用程序会崩溃。你可以多次重复说这是一个非常糟糕的设计,但这就是我所拥有的,我对此无能为力。这就是我所说的“实践”,与抽象真实但无用的陈述相反。
因此,我们的产品需要一种服务来告诉当前线程是否是进程的主线程。因此,我选择了一个非常基本的 DLL,它静态链接到所有 EXE 和几乎所有其他 DLL,并在那里实现了这样一个“服务”,正如 @MSalters 答案中所描述的那样。
这种方法一直运行良好,直到最近我开始收到来自另一个国家办事处的开发人员的投诉,称所选的 DLL 正在工作线程上加载(导致应用程序崩溃)。我们无法在办公室重现这种现象,因此我所拥有的只是调试器线程窗口的快照。以下是其中之一;如果它告诉某人一些事情,我会很高兴知道。
调试器可以显示哪个线程(如果有)源自 EXE 的入口点。这个入口点不是WinMain
,因为你的编译器需要入口点来设置它的运行时。该运行时调用您的
WinMain
。在DLL加载时(即
DllMain
)你的代码在Loader Lock下运行,此时你几乎无能为力。但是对
DLL_PROCESS_ATTACH
的调用专门发生在第一个线程上。因此,您无需执行任何操作即可找到该线程。您可以安全地使用
TlsAlloc
标记此线程以供将来使用。您也可以安全地调用
GetCurrentThreadId
,因为这是一个
kernel32
函数,它已经在
DllMain
之前加载了。