(静态链接的)DLL是否使用与主程序不同的堆?

我是Windows编程的新手,我刚刚“迷路”了两个小时,find了一个每个人似乎都知道的错误:你不能在一个DLL的堆中创build一个对象,并在另一个DLL(或主程序) 。

我几乎可以肯定,在Linux / Unix上,情况并非如此(如果是这样的话,请说出来,但是我敢肯定,我已经做了上千次,没有问题…)。

在这一点上,我有几个问题:

1)静态链接的DLL是否使用与主程序不同的堆?

2)静态链接的DLL是否映射到主程序的相同进程空间中? (我很确定这里的答案是一个大的YES,否则将主程序中的函数指针传递给DLL中的函数是没有意义的)。

我正在谈论普通/正规的DLL,而不是COM / ATL服务

编辑:“静态链接”我的意思是我不使用LoadLibrary加载DLL,但我链接到存根库

DLLs / exes将需要链接到C运行时库的实现。

对于C Windows运行时库,如果您希望链接到以下内容,则可以选择指定:

  1. 单线程C运行时间库(现在已经停止支持单线程库)
  2. 多线程DLL /多线程调试DLL
  3. 静态运行时库。
  4. 更少(可以查看链接)

它们中的每一个都会指向一个不同的堆,所以你不允许从一个运行时库的堆中获取地址。

现在,这取决于你正在谈论的DLL链接到哪个C运行时库。 假设您正在使用的DLL已链接到静态C运行时库,并且您的应用程序代码(包含主函数)已链接到多线程C运行时DLL,那么如果您将指针传递给分配给DLL到你的主程序,并试图释放它,反之亦然,它可能导致未定义的行为。 所以,基本的根本原因是C运行时库。 请仔细选择它们。

请在这里和这里找到有关C运行时库的更多信息

来自MSDN的引用:

小心 不要混合运行时库的静态和动态版本。 在进程中运行时库的多个副本可能会导致问题,因为一个副本中的静态数据不会与另一个副本共享。 链接程序可以阻止您在一个.exe文件中同时链接静态和动态版本,但是仍然可以以两个(或更多)运行时库的副本结束。 例如,与运行库的静态(非DLL)版本链接的动态链接库可能会导致问题时使用与运行时库的动态(DLL)版本链接的.exe文件。 (您也应该避免在一个进程中混合库的调试版和非调试版)。

如果我有一个编译为.exe的应用程序,并且我想要使用一个库,我可以从.lib文件静态链接该库或从.dll文件动态链接该库。

每个链接的模块(即每个.exe或.dll)将链接到C或C ++运行时的实现。 运行时间本身是一个库,可以静态或动态链接到不同的线程配置。

通过说静态链接的dll是否描述了一个应用程序.exe动态链接到一个库.dll和该库内部静态链接到运行时的设置? 我会认为这是你的意思。

另外值得注意的是,每个模块(.exe或.dll)都有自己的静态范围,即.exe中的全局静态不会与.dll中具有相同名称的全局静态相同的实例。

因此,在一般情况下,不能假定在不同模块中运行的代码使用的是运行时的相同实现,而且它们也不会使用任何静态的相同实例。

因此,在处理跨越模块边界的对象或指针时,需要遵守某些规则。 对于任何给定的地址,分配和释放必须在相同的模块中进行。 否则,堆不匹配,行为将不会被定义。

COM使用引用计数来解决这个问题,当引用计数达到零时,对象会自行删除。 这是用于解决匹配位置问题的常见模式。

其他问题可能存在,例如,Windows定义了某些动作,例如,如何在每个线程的基础上处理分配失败,而不是基于每个模块。 这意味着在由模块B设置的线程上的模块A中运行的代码也可能会遇到意外的行为。

首先了解Windows操作系统上的堆分配和堆栈以及我们的应用程序/ DLL。 传统上,操作系统和运行时库带有堆的实现。

  1. 在进程开始时,操作系统创建一个名为Process heap的默认堆。 如果没有使用其他堆,则使用进程堆分配块。
  2. 语言运行时间也可以在一个过程中创建单独的堆栈。 (例如,C运行时创建一个自己的堆。)
  3. 除了这些专用的堆,应用程序或许多加载的动态链接库(DLL)之一可能会创建和使用单独的堆,称为私有堆
  4. 这些堆位于所有虚拟内存系统中操作系统的虚拟内存管理器之上。
  5. 让我们来讨论更多关于CRT和相关的堆:
    1. C / C ++运行时(CRT)分配器:提供malloc()和free()以及新的和删除操作符。
    2. 作为其初始化的一部分,CRT为其所有分配创建了一个额外的堆(这个CRT堆的句柄在CRT库内部存储在一个名为_crtheap的全局变量中)。
    3. CRT创建自己的私人堆,它位于Windows堆顶部。
    4. Windows堆是围绕Windows运行时分配器(NTDLL)的一个薄层。
    5. Windows运行时分配器与虚拟内存分配器进行交互,虚拟内存分配器保留并提交操作系统使用的页面。

您的DLL和exe链接到多线程静态CRT库。 你创建的每个DLL和exe都有一个自己的堆,即_crtheap。 分配和解除分配必须从各自的堆中发生。 从动态分配的DLL,不能从可执行文件中取消分配,反之亦然。

你可以做什么? 编译我们的代码在DLL和EXE的使用/ MD或/ MDD使用运行时库的多线程特定和DLL特定版本。 因此,DLL和EXE都链接到相同的C运行时库,因此一个_crtheap。 分配总是与单个模块内的解除分配配对。