从 C++/CLI 调用 DLL 函数时的 PInvokeStackImbalance

PInvokeStackImbalance when calling a DLL function from C++/CLI

在处理 C++/CLI 项目以包装本机 C++ DLL 时,我遇到了一个接受 std::string 的本机函数。类似于以下内容:

class NativeApi
{
public:
  ErrorCode readFile(std::string filename = "path.csv");
};

在我的托管包装器实现中,我分配了一个新的原生实例 class 并调用了这个函数:

ref class ManagedApi
{
private:
  NativeApi *api;
public:
  ManagedApi(): api(new NativeApi()) { }
  void Read()
  {
    api->readFile("apath.csv") // or with nothing to use default value
  }
}

当我 运行 执行此操作时,我收到 MDA PinvokeStackImbalance 抱怨此调用使堆栈不平衡。我很惊讶,因为我唯一一次获得此 MDA 是在调用约定不匹配时来自 C#。我从未在 C++/CLI 中看到过这种情况,大概所有的匹配都是由编译器自动完成的。

有没有人见过这个?谷歌搜索是空的。我查看了 DLL 签名,它看起来像:

?readFile@NativeApi@@QAE?AW4ErrorCode@@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z

这告诉我函数在那里,并且将 basic_string 作为唯一参数,它应该匹配标准 std::string 类型定义。

不知道可能出了什么问题。我可以对不涉及字符串的本机 API 进行其他调用。

您使用的 std::string 的定义与用于编译本机 C++ DLL 的定义之间可能存在差异。即使定义相同,本机 DLL 也可能没有使用与您相同版本的 C 运行时,因此当您的 DLL 为 std::string 分配内存时,本机 DLL 将尝试调用 delete 在上面(当字符串在 readFile 方法的末尾被销毁时),并且对 delete 的调用将转到与分配对象不同的堆!

如果你想让它工作,你将不得不使用完全与在本机 DLL 上使用的相同版本的编译器。请注意,您将仅限于项目的发布版本,因为您没有使用调试运行时编译的本机 DLL。

此问题的 正确 修复是在跨 DLL 边界调用时使用原始类型(在本例中,wchar_t*)。如果您可以请求更改本机 DLL,我会这样做。如果只使用原始类型,那么使用不同的运行时就没有问题,并且一切都按应有的方式工作。