C++ 通过引用 dll 中的函数传递 std::string

C++ Passing std::string by reference to function in dll

提问人:Aleksey 提问时间:3/9/2014 最后编辑:Aleksey 更新时间:1/15/2020 访问量:9570

问:

我在通过引用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

_sFileNamein 函数设置任何值(例如,)。t4gs..\n\t

我尝试检测任何堆损坏,但编译器说没有错误。

DLL 已在调试设置中编译。.exe 程序也在调试中编译。

怎么了??帮助。。!

P.S. 我使用了 Visual Studio 2013。WinApp的。

编辑

我将func的标头更改为以下代码:

AFCLIBDLL_API EAFCErrors CAFC::CreateArchive(char const *const _pArchiveName)
{
    std::string _sArchiveName(_pArchiveName);
    ...

我真的不知道,如何修复这个错误......

关于堆:它被分配在我们进程的虚拟内存中,对吧?在这种情况下,共享虚拟内存很常见。

C++ 字符串 C++11 DLL 按引用传递

评论

0赞 cf- 3/9/2014
EXE 和 DLL 是否使用完全相同的编译器编译,使用完全相同的标志?否则,EXE 和 DLL 之间的定义和其他 STL 类型可能不同,这可能会导致问题。std::string
0赞 M.M 3/9/2014
我建议您保持 DLL 使用定义良好的 ABI 。通过DLL接口传递分配的内存会带来麻烦。例如,DLL 和调用应用程序可能未使用相同的堆。
0赞 zneak 3/9/2014
@MattMcNabb,如果他们不使用相同的堆,为什么会成为问题?它们仍然共享相同的地址空间,对吧?
0赞 M.M 3/9/2014
好吧,如果他添加到字符串中并且它释放了它的内存并分配了新的内存,那么另一个堆将无法释放,因为它不知道该块,等等。
0赞 Aleksey 3/9/2014
basic_string(const _Elem *_Ptr)- 它是在 .c_str() 之后调用的。 在输入函数的那一刻是正确的:“data\\gtasa.afc”_Ptr

答:

10赞 PaulMcKenzie 3/9/2014 #1

该问题与 STL 关系不大,而与跨应用程序边界传递对象有关。

1) DLL 和 EXE 必须使用相同的项目设置进行编译。您必须这样做,以便结构对齐和打包相同,成员和成员函数没有不同的行为,甚至更微妙的是,引用和引用参数的低级实现完全相同。

2) DLL 和 EXE 必须使用相同的运行时堆。为此,必须使用运行时库的 DLL 版本。

如果您创建的类执行与 类似的操作(在内存管理方面),则会遇到相同的问题。std::string

内存损坏的原因可能是相关对象(在本例中)分配和管理动态分配的内存。如果应用程序使用一个堆,而 DLL 使用另一个堆,那么如果您实例化了 DLL,但应用程序正在调整字符串的大小(这意味着可能会发生内存分配),这将如何工作?std::stringstd::string

评论

0赞 Aleksey 3/9/2014
因此,我不使用任何其他内存分配管理器,除了标准。DLL 和 EXE 是使用完全相同的设置编译的。
1赞 David Heffernan 3/9/2014
它不止于此。您需要两个模块才能使用相同的运行时实例。
0赞 Aleksey 3/9/2014
运行时的同一实例...这是怎麽?
0赞 David Heffernan 3/9/2014
确实是的。同一个实例。
0赞 PaulMcKenzie 3/10/2014
@Aleksey - 在项目设置中,必须将运行时设置为运行时的 DLL 版本。如果你不知道这一点,那么你需要知道,因为这对于你的代码工作是绝对必要的。
5赞 David Heffernan 3/9/2014 #2

像 C++ 这样的类可以跨模块边界使用,但这样做会对模块产生重大限制。简单地说,两个模块必须使用相同的运行时实例std::string

因此,例如,如果您使用 VS2013 编译一个模块,那么您必须为另一个模块执行此操作。此外,您必须链接到动态运行时,而不是静态链接运行时。后者导致每个模块中具有不同的运行时实例。

看起来您正在导出成员函数。这也需要一个通用的共享运行时。你应该对整个班级而不是单个成员使用。__declspec(dllexport)

如果您同时控制这两个模块,那么就很容易满足这些要求。如果您希望让其他方生成一个或另一个模块,那么您就对这些其他方施加了重大限制。如果这是一个问题,请考虑使用更可移植的互操作。例如,而不是使用 .std::stringconst char*


现在,您可能已经在使用动态运行时的单个共享实例。在这种情况下,错误将更加平淡无奇。也许调用约定不匹配。鉴于你的问题中的细节很少,很难肯定地说任何事情。

评论

0赞 marko 3/9/2014
正是由于这个原因,COM 定义了一组关于对象所有权的狭窄(相当不方便的)类型和规则。我们可能建议对 OP 进行研究,因为它为这个问题提供了一个强有力的解决方案。另外值得指出的是,在 Windows 上使用 STL 时,传统的发布和调试版本是不兼容的(这是因为在构建调试时需要额外的健全性检查,导致对象的大小和布局不同)。
0赞 David Heffernan 3/9/2014
@Marko 这包含在“使用 RTL 的合理实例”中。COM 在这里将是一个不错的选择。二进制互操作。
1赞 wathson 7/1/2017 #3

我遇到了类似的问题。 我解决了它同步配置属性->C / C++设置。

如果需要调试模式:

  • 在两个项目的预处理器定义中设置定义。_DEBUG
  • 在两个项目的“代码生成 -> 运行时库”中设置 /MDd。

如果需要发布模式:

  • 删除两个项目的预处理器定义中的定义。_DEBUG
  • 在两个项目的“代码生成 -> 运行时库”中设置 /MD。

这两个项目我的意思是exe和dll项目。
它对我有用,特别是如果我不想更改dll的任何设置,而只是调整它们。