从 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,我会这样做。如果只使用原始类型,那么使用不同的运行时就没有问题,并且一切都按应有的方式工作。
在处理 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,我会这样做。如果只使用原始类型,那么使用不同的运行时就没有问题,并且一切都按应有的方式工作。