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的主要内容,如果未能解决你的问题,请参考以下文章

如何通过引用从 c# 到 c++ 传递字节数组

通过引用传递的 std::vector 不是从函数传递到 main()

vb.net中的C ++ DLL Wrapper传递字节数组的字节数组?

C#中的引用传递值传递。

C ++性能:通过引用传递对象vs函数

c语言里一般都说引用而不说使用,引用和使用有啥区别呢