正确链接的DLL和EXE应该有一个freestore,它们都可以从中分配基于堆的对象。以下是Chis Becke在Who allocates heap to a DLL?中的答案:
... C ++运行时负责创建freestore和deciding 如何分配它。 具体来说,如果您使用Dll运行时选项,则单个dll - msvcrtxx.dll - 管理单个 所有dll和exe之间共享的freestore,与该dll链接
由于这是真的,那么我应该能够在其他DLL / EXE中定义的DLL / EXE中new
个对象。根据Chris的说法,msvcrtxx.dll
和编译时/运行时链接器可以处理所有DLL / EXE的联合freestore的位置。
这对我不起作用。
为了测试这个,我生成了两个MFC对话框程序:NewFailMfc1和NewFailMfc2。在执行NewFailMfc2
时,正在运行NewFailMfc1
访问Www
的{{1}}功能失败。
new
有更好地了解DLL / EXE freestore如何工作的人知道问题是什么吗?
(我试图在“全局函数// Code in NewFailMfc1.
void Www()
{
char* ch { nullptr };
ch = new char[ 100 ]; // error: attempts to allocate memory somewhere else than in the prescribed joint DLL/EXE freestore
ch[ 0 ] = '\0';
}
// Calling code in NewFailMfc2.
Www();
在::operator new
和MyApp1
中编译失败时问过一次这个问题。在询问过程中,我发现问题出现了更多通常比在MyApp2
std lib中。)
EDIT1:
在MSDN中,一个不错的虚拟代理找到了我Potential Errors Passing CRT Objects Across DLL Boundaries。不幸的是,它推荐的唯一解决方案是使用<random>
编译器选项编译所有程序,而不是/MD
使用CRT的多个副本自动导致跨越边界和内存访问违规。
这对像我这样的应用开发者来说不是好消息。我需要的是一个最佳实践,所以我可以应用它并满足我的交付期限,而不必处理神秘的低级内存问题。如何知道/MT
类型中的全局::operator new
隐藏了一个隐藏号码?直到它被访问违反我才会这样做。在我完成所有这些研究后,我才意识到通过它调用全局std:random_device
,它是越过边界,这给了我的DLL / EXE一个访问冲突。很晦涩。
EDIT2:
我在Visual Studio中提交了有关std :: random_device实现的错误报告。请参阅“std :: random_device实例化在某些情况下会导致访问冲突”。
答案 0 :(得分:1)
当您分配内存时,实际上CRT可以分配比您要求的更多一点。例如。流行的做法(至少在过去)是多分配4个字节(用系统位数代替),在开头写入分配的内存大小并返回ptr + 4
给你。因此,当您释放内存时,系统会知道应该释放多少内存。
这是一个简化的图片。不同的编译器,相同编译器的不同版本和相同编译器相同版本的不同配置可以不同地做到这一点。例如。 debug config可以使用一些填充来检测缓冲区溢出和其他技巧。因此,当您在一个二进制文件中分配内存并在另一个二进制文件中释放时,如果使用不同的编译器,则可能导致内存损坏(在最佳情况下立即崩溃)。
这个和许多其他原因导致了一个共同的建议:在你分配它的二进制文件中释放内存。这通常是通过提供API类的Release
成员函数并将析构函数设为私有,或通过unique_ptr
(或shared_ptr
)使用自定义删除或其他技术来实现的。
现在关于/MD
建议。 /MD
表示动态CRT(=在dll中),并且由于在同一进程中无法两次加载相同的dll,这意味着将使用相同的CRT进行分配和释放。对于不同版本或不同的编译器,这仍然不是解决方案。例如。许多应用程序使用插件系统,在这种情况下,要求所有插件都由特定的编译器/ version / config编译是不是一个好主意
答案 1 :(得分:1)
多线程调试DLL堆将是每个进程,因此即使两个应用程序都与多线程调试DLL链接,NewFailMfc1和NewFailMfc2也将拥有自己的私有堆。 使用多线程调试DLL堆只能解决跨同一进程地址空间中的多个堆跨越边界的问题,并且不是可用于跨进程边界共享堆的机制。
答案 2 :(得分:0)
显式实例化强制编译器为模板化类或函数的特定参数列表生成代码。如果没有该代码,我导入的DLL/EXE
二进制文件在运行时实例化失败,例如ch = new char[ 100 ]
和std::random_device rd;
,它隐含地执行全局::operator new
。我没有发现为什么会发生这种情况的有用解释。遗憾的是,IPC讨论没有明确区分涉及多个正在运行的进程的客户端 - 服务器运行时,以及导入在其他地方编译和导出的二进制代码(DLL/EXE
)的运行时代码。
解决方案是将模板参数添加到失败的类中,并将.cpp文件添加到显式实例化类的每个模块,如MyClsModule1.cpp
,MyClsModule2.cpp
等。在这些文件中我明确地实例化该模块的类。我还为包含.h
s的每个模块添加MyClsModule1.h
文件,如MyClsModule2.h
,extern
,因此在特定模块中不会出现重复代码生成。使用这种方法,每个模块在编译时生成特定于模块的类代码,这会强制模块的线程允许访问该模块的进程堆的堆实例化。
这个现代C ++解决方案对我而言非常优雅,因为它使我不必将COM
等复杂的IPC解决方案重新引入到我的应用程序代码中。
从应用程序开发人员的角度来看,我认为我的原始代码应该有效或者至少产生了错误,这些错误可以提供更多关于问题的信息,所以我要留下 EDIT2中提到的错误报告强>有效。
答案 3 :(得分:-1)
忘了这一切。如果这个工作以某种方式只是你的运气。
更好的方法是运行COM
进行内存共享。请在此处查看IMalloc
示例。
ftp://210.212.172.242/Digital_Library/CSE/Computer,%20Technology%20and%20Engineering%20eBooks/Books7/petzold_rus.part1%20(2)/DISK/CODE/CHAP20/
不是那么容易。
如本规范前面所述,当所有权时 分配的内存通过接口传递,COM需要 内存分配特定.... http://www.opengroup.org/comsource/techref2/CHP05GDC.HTM
想提一下,在发布的样本(win95次)中,你正在收集IMalloc
ole
地面接口。它可能被视为MS Windows源代码的一部分。想点什么关于今天的内置IMalloc
,不确定它是否相同。