C++传递std::string引用到dll函数中

9
我遇到了将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程序也以调试方式编译。
有什么问题吗?帮帮忙..!
P.S. 我使用的是Visual Studio 2013。WinApp。
编辑
我已经更改了函数的头文件为以下代码:
AFCLIBDLL_API EAFCErrors CAFC::CreateArchive(char const *const _pArchiveName)
{
    std::string _sArchiveName(_pArchiveName);
    ...

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

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


EXE和DLL是使用完全相同的编译器和标志编译的吗?否则,std::string和其他STL类型的定义可能在EXE和DLL之间不同,这可能会导致问题。 - cf-
我建议您将DLL保持使用明确定义的ABI。在DLL接口之间传递分配的内存会引发问题。例如,DLL和调用应用程序可能不使用相同的堆。 - M.M
@MattMcNabb,如果它们没有使用相同的堆,为什么会成为问题呢?它们仍然共享相同的地址空间,对吧? - zneak
如果他向字符串添加内容并释放其内存并分配新的内存,那么另一个堆将无法释放,因为它不知道该块等等。 - M.M
basic_string(const _Elem *_Ptr) - 它是在.c_str()之后被调用的。在进入函数时_Ptr的值正确:"data\gtasa.afc"。 - Aleksey
3个回答

10
该问题与STL无关,与跨应用程序边界传递对象有关。
1) DLL和EXE必须使用相同的项目设置进行编译。这样做是为了使结构体对齐和打包相同,成员和成员函数没有不同的行为,甚至更微妙的是,引用和引用参数的低级实现完全相同。
2) DLL和EXE必须使用相同的运行时堆栈。为此,您必须使用运行时库的DLL版本。
如果您创建了一个执行类似于std :: string的事情(在内存管理方面),那么您将遇到相同的问题。
可能造成内存破坏的原因是所讨论的对象(在本例中为std :: string)分配和管理动态分配的内存。如果应用程序使用一个堆栈,而DLL使用另一个堆栈,则如果您在DLL中实例化std :: string,但应用程序正在调整字符串的大小(意味着可能发生内存分配),该怎么办?

所以,除了标准的内存分配管理器之外,我没有使用任何其他的内存分配管理器。DLL 和 EXE 是使用完全相同的设置编译的。 - Aleksey
1
这不仅仅是如此。您需要同时使用两个模块来使用相同的运行时实例。 - David Heffernan
相同的运行时实例...这是什么? - Aleksey
是的,确实如此。同一实例。 - David Heffernan
@Aleksey - 在你的项目设置中,你必须将运行时设置为运行时的DLL版本。如果你不知道这一点,那么你需要知道,因为这对于你的代码正常工作是绝对必要的。 - PaulMcKenzie

5
C++类,如std::string可以跨模块边界使用,但这样做会对模块产生重大约束。简单地说,两个模块必须使用相同的运行时实例。例如,如果您使用VS2013编译一个模块,那么您必须为另一个模块这样做。此外,您必须链接到动态运行时,而不是静态链接运行时。后者导致每个模块中都有不同的运行时实例。此外,看起来您正在导出成员函数,这也需要共享运行时。您应该在整个类上使用__declspec(dllexport)而不是个别成员。如果您控制着两个模块,那么很容易满足这些要求。如果您希望让其他方生产其中之一的模块,则对这些其他方施加了重大限制。如果这是一个问题,那么请考虑使用更便携的交互操作。例如,使用const char*代替std::string。现在,您可能已经在使用单个共享的动态运行时实例。在这种情况下,错误将更加平凡。也许调用约定不匹配。鉴于您的问题缺乏详细信息,很难确定任何事情。

正是因为这个原因,COM定义了一组狭窄的(相当不方便的)类型和关于对象所有权的规则。建议OP研究这些约定,因为它提供了一个强大的解决方案来解决这个问题。另外值得指出的是,在Windows上使用STL时,传统的发布版和调试版是不兼容的(这是因为在构建调试版时会进行额外的健全性检查,导致对象大小和布局不同)。 - marko
@Marko 这可以通过“使用RTL的相同实例”来解决。在这里,COM可能是一个不错的选择。二进制互操作。 - David Heffernan

1
我遇到了类似的问题。 我通过同步“配置属性 -> C / C ++”设置来解决它。
如果您想要调试模式: - 在两个项目中的“预处理器定义”中设置“_DEBUG”定义。 - 在两个项目的“代码生成 -> 运行时库”中设置/ MDd。
如果您想要发布模式: - 在两个项目的“预处理器定义”中删除“_DEBUG”定义。 - 在两个项目的“代码生成 -> 运行时库”中设置/ MD。
我指的是可执行文件和dll项目。 这对我很有用,特别是如果我不想改变dll的任何设置,只需调整即可。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接