c-HINSTANCE在线程之间有效吗



在一个.exe应用程序中,WinMain入口点有一个HINSTANCE参数,它应该是一个伪句柄(因为根据MSDN,相当于返回伪句柄的GetModuleHandle(NULL))。我认为它是,因为既有特殊值(例如NULL到平均入口点模块),也有用于返回错误的常量(小于32的任何值)。

MSDN明确地将其描述为指向模块基地址的指针(现在相当于HMODULE);我们知道这对16位应用程序可能有完全不同的意义,但在32/64位的世界中,每个进程都有自己的地址空间,那么它的精确值是无用的,对于每个实例可能总是相同的,在其进程之外绝对没有意义。

所有这些都表明,这是我的第一个问题:我们能(正式地,尽管MSDN似乎是矛盾的)假设HINSTANCE是一个指针吗(即使老实说,我认为这没有任何用处),还是最好假设它是一个(伪)句柄(其值为不透明)?

让我们假设它的值是不透明的,我的第二个问题:它的值对每个进程有效还是对每个线程有效?

我们可能认为进程句柄对每个进程都有效,但很少有(角落)情况让我认为我们应该假设它对每个线程都有效。如果存在这些角落案例,那么(即使通常每个流程都像预期的那样运行)我们依赖于一个实现细节,一个未定义的行为,可能会因不同的体系结构、版本和环境而发生变化。

让我们看看我看到一个问题的例子(代码太琐碎了,我只描述了场景):DLL通常有一个共享代码部分,但它们也可能有(即使这很不常见)共享数据部分(例如,在进程之间共享扩展的数据,或实现快速而肮脏的IPC机制)。它可能不常见,但也是可能的(而且很容易实现,例如在VC++中,只有很少的#pragmadata_segcomment(linker)指令)。在这种情况下,我们知道(在我们的DLL中)我们无法比较HINSTANCEs(因为它们可能具有相同的值),但我们也不能信任我们从线程A中获得的HINSTANCE(在DLL中)与线程B中的HINSTANCE具有可比性。简而言之:每次我们需要HINSTANCE时,我们都必须调用GetModuleHandle(NULL)来获得每个线程的实际有效值。

作为奖金,我也理解这是如何应用于HINSTANCE的,当它来自WinMain时,我们将其作为参数。理论上,它是每个线程的,但例如,CreateWindow()会正确解析它(因为作用域是当前执行进程,当然假设调用线程有自己的消息循环)?

void createToolbox(const char* windowName, HINSTANCE hInstance) {
// ...
// Do we need this?
// HINSTANCE hInstance = GetModuleHandle(NULL);
CreateWindow(windowClass, windowName,
WS_OVERLAPPEDWINDOW,
0, 0, 640, 480,
0, 0, hInstance, NULL);
// ...
}

EDIT我似乎完全错了,我确实记得HMODULE的线程亲和力,但它适用于Windows Mobile。。。

如果此参数为NULL,GetModuleHandle将向当前进程返回一个伪句柄。[…]伪句柄是一个特殊的常量,它被解释为当前线程句柄。当需要线程句柄时,调用线程可以使用此句柄来指定自己。

显然,对于桌面应用程序(以及其他差异)来说,这不是真的。

流程中的每个模块都有一个模块句柄,该句柄也是该模块的基地址。传递给WinMainhInstance自变量是进程主模块的基地址。因此,它在整个进程中都是有效的,因为进程只有一个虚拟地址空间。

传递给WinMainhInstance自变量总是等于GetModuleHandle(NULL)

如果愿意,通常可以将模块句柄视为不透明。也就是说,通常不需要取消引用指针,只需将其传递给需要HMODULE参数的API函数。这些都不会改变该值在不同线程中是否有效。该值是每个进程的值。

我搞不懂你的奖金问题。我怀疑这源于一个错误的假设,即模块句柄是每个线程的。一旦您接受模块句柄在进程中的所有线程中都具有相同的含义,那么您的绝大多数问题就迎刃而解了。

HINSTANCE记录为:

一个实例的句柄。这是内存中模块的基地址。

传递给WinMainhInstance参数是用于创建进程的模块的基地址。它在流程的整个生命周期中都是有效的。由于它是一个指针,所以它不具有线程亲和性,并且可以在线程之间自由传递。事实上,在操作系统在进程中创建单个线程之前,这个指针就已经存在了。

"特殊值">不是HINSTANCE数据类型的属性。它们是ShellExecute:合同的一部分

为了与16位Windows应用程序向后兼容,返回值被强制转换为HINSTANCE。然而,这并不是一个真正的HINSTANCE。

额外阅读:如何处理ShellExecute函数返回的HINSTANCE?

我在MSDN中没有提到GetModuleHandle()在任何情况下都会返回伪句柄(与GetCurrentProcess()/GetCurrentThread()不同)。因此,模块句柄不存在线程亲和性这一概念。它们都是进程范围的,这就是GetModuleHandle()的文档警告线程问题的具体原因。

作为一个指向模块基地址的指针只能证实这一点,因为你提到了一些原因。

所以,对于你的第一个问题,我回答说,如果它们被记录为指针,那么假设它们是指针是安全的。否则,总是安全的,将它们视为不透明句柄,并知道它们从来都不是手柄。

对于你的第二个问题,我确认这是按流程提出的。

对于您的场景,我认为由于GetModuleHandle(NULL)从DLL中是无用的,因此最简单的方法是将传递给DllMain()的模块句柄存储在该DLL的非共享全局变量中。

至于你的奖金问题,是的。

我不知道是什么让你相信模块句柄具有线程亲和性

最新更新