我是Windows编程的新手,我只是“迷失”了两个小时寻找一个每个人都知道的错误:你不能在DLL中的堆上创建一个对象并在另一个DLL中(或在主程序)。
我几乎可以肯定,在Linux / Unix上,情况并非如此(如果是,请说出来,但我很确定我做了数千次而没有问题......)。
此时我有几个问题:
1)静态链接的DLL使用与主程序不同的堆吗?
2)静态链接的DLL是否映射在主程序的同一进程空间中? (我很确定这里的答案是一个很大的问题,否则将主程序中的函数指针传递给DLL中的函数是没有意义的。)
我说的是普通/常规DLL,而不是COM / ATL服务
编辑:通过“静态链接”我的意思是我不使用LoadLibrary加载DLL但我链接到存根库
答案 0 :(得分:20)
DLL / exes需要链接到C运行时库的实现。
对于C Windows运行时库,如果要链接到以下内容,可以选择指定:
它们中的每一个都将引用不同的堆,因此不允许将从一个运行时库的堆中获取的传递地址传递给其他堆。
现在,它取决于您正在谈论的DLL链接到哪个C运行时库。假设让你说,你正在使用的DLL已链接到静态C运行时库,你的应用程序代码(包含主函数)已经链接到多线程C运行时DLL,然后如果你将指针传递给分配给它的内存DLL到您的主程序并尝试在那里释放它,反之亦然,它可能导致未定义的行为。因此,基本的根本原因是C运行时库。请仔细选择。
请查看有关支持的C运行时库的更多信息here& here
来自MSDN的引用:
警告 不要混用运行时库的静态和动态版本。在进程中拥有多个运行时库副本可能会导致问题,因为一个副本中的静态数据不会与另一个副本共享。链接器阻止您在一个.exe文件中链接静态和动态版本,但您仍然可以使用两个(或更多)运行时库副本。例如,与运行时库的静态(非DLL)版本链接的动态链接库与与运行时库的动态(DLL)版本链接的.exe文件一起使用时可能会导致问题。 (您还应该避免在一个进程中混合库的调试版本和非调试版本。)
答案 1 :(得分:4)
如果我有一个编译为.exe的应用程序,并且我想使用库,我可以静态地从.lib文件链接该库,或者从.dll文件动态链接该库。
每个链接的模块(即每个.exe或.dll)将链接到C或C ++运行时的实现。运行时本身是一个库,可以静态或动态链接到不同的线程配置。
通过说静态链接的dll ,您是否描述了一个应用程序.exe动态链接到库.dll并且该库内部静态链接到运行时的设置?我会假设这就是你的意思。
另外值得注意的是,每个模块(.exe或.dll)都有自己的静态范围,即.exe中的全局静态与.dll中具有相同名称的全局静态不是同一个实例。
因此,在一般情况下,不能假设在不同模块内运行的代码行使用相同的运行时实现,而且它们不会使用任何静态的相同实例。
因此,在处理跨越模块边界的对象或指针时,需要遵守某些规则。对于任何给定的地址,分配和解除分配必须在同一模块中进行。否则堆将不匹配,并且不会定义行为。
COM使用引用计数解决了这个问题,当引用计数达到零时,对象会自行删除。这是用于解决匹配位置问题的常见模式。
可能存在其他问题,例如Windows定义某些操作,例如如何在每个线程的基础上处理分配失败,而不是基于每个模块。这意味着在模块B设置的线程上运行在模块A中的代码也可能会遇到意外行为。
答案 2 :(得分:4)
让我们首先了解Windows OS上的堆分配和堆栈以及我们的应用程序/ DLL。传统上,操作系统和运行时库都带有堆的实现。
您的DLL和exe链接到多线程静态CRT库。您创建的每个DLL和exe都有自己的堆,即_crtheap。分配和取消分配必须从各自的堆发生。从DLL动态分配的,不能从可执行文件中解除分配,反之亦然。
你能做什么?在DLL和exe中使用/ MD或/ MDd编译我们的代码,以使用运行时库的多线程特定和DLL特定版本。因此,DLL和exe都链接到相同的C运行时库,因此一个_crtheap。分配始终与单个模块中的取消分配配对。