C ++通过引用dll中的函数传递std :: string
Posted
技术标签:
【中文标题】C ++通过引用dll中的函数传递std :: string【英文标题】:C++ Passing std::string by reference to function in dll 【发布时间】:2014-04-12 07:26:13 【问题描述】:我在通过引用 std::string 传递给 dll 中的函数时遇到问题。
这是函数调用:
CAFC AFCArchive;
std::string sSSS = std::string("data\\gtasa.afc");
AFCER_PRINT_RET(AFCArchive.OpenArchive(sSSS.c_str()));
//AFCER_PRINT_RET(AFCArchive.OpenArchive(sSSS));
//AFCER_PRINT_RET(AFCArchive.OpenArchive("data\\gtasa.afc"));
这是函数头:
#define AFCLIBDLL_API __declspec(dllimport)
AFCLIBDLL_API EAFCErrors CAFC::OpenArchive(std::string const &_sFileName);
我尝试通过调用函数逐步调试并查看函数内部的_sFileName
值。
函数中的_sFileName
设置任何值(例如,t4gs..\n\t
)。
我尝试检测任何堆损坏,但编译器说没有错误。
DLL 已在调试设置中编译。 .exe程序也在调试中编译。
怎么了??救命..!
附:我用的是 Visual Studio 2013。WinApp。
编辑
我已将 func 的标头更改为此代码:
AFCLIBDLL_API EAFCErrors CAFC::CreateArchive(char const *const _pArchiveName)
std::string _sArchiveName(_pArchiveName);
...
我真的不知道,如何修复这个错误......
关于堆:它分配在我们进程的虚拟内存中,对吧?在这种情况下,共享虚拟内存很常见。
【问题讨论】:
EXE 和 DLL 是否使用完全相同的编译器编译,使用完全相同的标志?否则,std::string
和其他 STL 类型的定义可能在 EXE 和 DLL 之间有所不同,这可能会导致问题。
我建议您保持 DLL 使用定义明确的 ABI 。通过 DLL 接口传递分配的内存是自找麻烦。例如,DLL 和调用应用程序可能没有使用同一个堆。
@MattMcNabb,如果他们不使用相同的堆,为什么会有问题?它们仍然共享相同的地址空间,对吧?
好吧,如果他添加到字符串并释放它的内存并分配新内存,另一个堆将无法释放,因为它不知道那个块,等等。
basic_string(const _Elem *_Ptr)
- 它是在 .c_str() 之后调用的。 _Ptr
在进入函数的那一刻是对的:“data\\gtasa.afc”
【参考方案1】:
这个问题与 STL 几乎没有关系,而与跨应用程序边界传递对象有关。
1) DLL 和 EXE 必须使用相同的项目设置进行编译。您必须这样做,以便结构对齐和打包相同,成员和成员函数没有不同的行为,更微妙的是,引用和引用参数的低级实现完全相同。
2) DLL 和 EXE 必须使用相同的运行时堆。为此,您必须使用运行时库的 DLL 版本。
如果您创建了一个与std::string
执行类似操作(在内存管理方面)的类,您会遇到同样的问题。
内存损坏的原因可能是有问题的对象(在这种情况下为std::string
)分配和管理动态分配的内存。如果应用程序使用一个堆,而 DLL 使用另一个堆,如果您在 DLL 中实例化 std::string
,但应用程序正在调整字符串的大小(这意味着可能发生内存分配),这将如何工作?
【讨论】:
所以,我不使用任何其他内存分配管理器,除了标准。 DLL 和 EXE 使用完全相同的设置进行编译。 不止于此。您需要两个模块使用相同的运行时实例。 运行时的同一个实例...这是什么? 确实如此。同一个实例。 @Aleksey - 在您的项目设置中,您必须将运行时设置为运行时的 DLL 版本。如果您不知道这一点,那么您需要知道,因为这对于您的代码正常工作是绝对必要的。【参考方案2】:我遇到了类似的问题。 我通过同步 Configuration Properties -> C / C++ 设置解决了它。
如果你想要调试模式:
在两个项目的预处理器定义中设置_DEBUG
定义。
在代码生成中设置 /MDd -> 两个项目中的运行时库。
如果你想要发布模式:
删除两个项目中预处理器定义中的_DEBUG
定义。
在代码生成 -> 两个项目中的运行时库中设置 /MD。
我的意思是 exe 和 dll 项目。 它对我有用,特别是如果我不想更改 dll 的任何设置而只想调整它们。
【讨论】:
【参考方案3】:像std::string
这样的C++ 类可以跨模块边界使用,但是这样做会给模块带来很大的限制。简单地说,两个模块必须使用相同的运行时instance。
因此,例如,如果您使用 VS2013 编译一个模块,那么您必须为另一个模块这样做。更重要的是,您必须链接到动态运行时,而不是静态链接运行时。后者会在每个模块中产生不同的运行时实例。
看起来您正在导出成员函数。这也需要一个通用的共享运行时。你应该在整个班级而不是个别成员上使用__declspec(dllexport)
。
如果您控制这两个模块,那么很容易满足这些要求。如果您希望让其他方生产一个或其他模块,那么您正在对这些其他方施加重大限制。如果这是一个问题,那么考虑使用更便携的互操作。例如,使用 const char*
代替 std::string
。
现在,您可能已经在使用动态运行时的单个共享实例。在这种情况下,错误将更加平淡无奇。也许调用约定不匹配。鉴于您的问题中的细节很少,很难肯定地说出任何事情。
【讨论】:
正是由于这个原因,COM 定义了一组狭窄的(相当不方便的)类型和关于对象所有权的规则。我们可能会建议 OP 研究这些约定,因为它为这个问题提供了一个强大的解决方案。还值得指出的是,在 Windows 上使用 STL 时,传统的发布和调试版本是不兼容的(这是因为在为调试而构建时额外的健全性检查,导致对象的大小和布局不同)。 @Marko 这已被“使用 RTL 的健全实例”所涵盖。 COM在这里将是一个不错的选择。二进制互操作。以上是关于C ++通过引用dll中的函数传递std :: string的主要内容,如果未能解决你的问题,请参考以下文章
通过引用传递的 std::vector 不是从函数传递到 main()