HINSTANCE在线程间是否有效?

时间:2015-04-23 08:22:40

标签: c windows winapi

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

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

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

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

我们可能认为进程句柄对于每个进程都是有效的,但很少(角落)情况让我认为我们应该假设它是有效的每个线程。如果存在这些极端情况(即使通常它似乎按预期工作 每个进程),我们依赖于实现细节,未定义的行为可能会发生变化不同的架构,版本,环境。

让我们看一个案例,我看到一个问题(代码是如此微不足道,我只是描述场景):DLL通常有共享代码段,但它们也可能(即使它非常罕见)共享数据部分(例如跨进程共享扩展数据或实现快速而脏的IPC机制)。它可能不常见但可能(并且很容易实现,例如在VC ++中,很少#pragmadata_segcomment(linker)指令。在这种情况下,我们知道(在我们的DLL中)我们无法比较HINSTANCE s(因为它们可能具有相同的值),但IMO我们也不能信任 {{1我们从线程A(在我们的DLL中)得到的是可比较的到我们在线程B中的HINSTANCE。简而言之:每次我们需要一个HINSTANCE我们必须调用HINSTANCE获取实际每线程有效的

作为奖励,我也会理解GetModuleHandle(NULL)如果它来自HINSTANCE,我们会将其作为参数。理论上它是每线程的,但是,例如,WinMain将正确解析它(因为范围是当前正在执行的进程,当然假设调用线程有自己的消息循环)?

CreateWindow()

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

  

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

显然,桌面应用程序不存在这种情况(除其他差异外)。

3 个答案:

答案 0 :(得分:2)

进程中的每个模块都有一个模块句柄,它也是该模块的基址。传递给hInstance的{​​{1}}参数是进程主模块的基址。因此,它在整个过程中都是有效的,因为该过程只有一个虚拟地址空间。

传递给WinMain的{​​{1}}参数始终等于hInstance

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

我无法理解你的奖金问题。我怀疑这是因为模块处理的错误假设是每个线程。一旦你接受一个模块句柄在一个过程中的所有线程中具有相同的含义,你的绝大多数问题就会消失。

答案 1 :(得分:2)

HINSTANCE记录为:

  

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

传递给hInstance的{​​{1}}参数是用于创建进程的模块的基址。它在整个过程的生命周期内都有效。由于它是一个指针,它没有线程亲和力,可以在线程之间自由传递。实际上,在OS在进程中创建单个线程之前,该指针已经存在。

"特殊值" 不是WinMain数据类型的属性。它们是ShellExecute

合同的一部分
  

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

奖金阅读:What can I do with the HINSTANCE returned by the ShellExecute function?

答案 2 :(得分:1)

我认为MSDN中任何地方都没有提及GetModuleHandle()在任何情况下都返回伪句柄(与GetCurrentProcess() / GetCurrentThread()不同)。因此,模块句柄没有线程亲和性。它们都是整个流程范围内的,这就是GetModuleHandle()的文档警告线程问题的具体原因。

作为指向模块基地址的指针,只会因为你引发的原因而确认它。

所以,对于你的第一个问题,如果他们被证明是这样的话,我可以安全地认为它们是指针。否则,它的总是安全地将它们视为不透明句柄,并且知道它们永远不会是句柄。

关于你的第二个问题,我确认了它的每个过程。

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

关于你的奖金问题,是的,它会起作用。

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