2011-10-05 57 views
1

我的问题是,我有一些DLL中的函数其中的一些功能,例如:C++中的字符串?

#include <string> 
using namespace std; 
extern "C" __declspec(dllexport) string __cdecl encryption(string s) 
{ 
return s; 
} 

每当我试图调用从C#这个功能,这里使用的代码IM:

[DllImport("Packer.dll", EntryPoint = "encryption")] 
static extern string encryption(string s); 

我收到一个错误: 对PInvoke函数'Packer'的调用使堆栈不平衡。这很可能是因为托管的PInvoke签名与非托管目标签名不匹配。检查PInvoke签名的调用约定和参数是否与目标非托管签名相匹配。

即时猜测,因为我没有对功能 权声明任何人都可以指导我如何解决这个问题,在此先感谢我得到这个错误

+6

这就是为什么DLL文件应该有Ç的API,而不是C++的API。每个人都可以同意“const char *”的二进制接口是什么。 –

+0

您还需要在C#代码上设置调用声明或更改C++调用约定。 –

+0

@Adam,几乎没有,扔在Unicode和所有地狱打破了松散:) – Blindy

回答

2

如果没记错,如果不指定,否则P /调用假定调用约定为__stdcall。如果是这样,将__cdecl更改为__stdcall应解决第一个问题。 正如@Adam Rosenfield指出的那样,您可能还需要传递并返回char const *,而不是string。 C#和C++几乎肯定对构成字符串的构成有一些不同的想法。

+0

也许最好是改变C#声明的调用约定。 –

+0

我收到此错误:无法在DLL'Packer.dll'中找到名为'encryption'的入口点。编辑:我会尝试作为杰夫梅尔卡多说编辑2:尝试从C#声明做到这一点,但我得到了这个调用PInvoke函数'打包'已经不平衡的堆栈。这很可能是因为托管的PInvoke签名与非托管目标签名不匹配。检查PInvoke签名的调用约定和参数是否与目标非托管签名相匹配。 – method

+0

@ user959615:是的,默认情况下,stdcall会添加一个表示参数大小的后缀,所以它会变成'encryption @ 4'。您可以使用链接器[模块定义文件](http://msdn.microsoft.com/zh-cn/library/hyx1zcd3.aspx)消除该问题。 –

7

std::string不能与PInvoke一起使用,因为C++没有适当清理堆栈,复制对象等所需的ABI对象。这是C++最大的难题之一。

您必须使用char*指针和普通的C API。简而言之,PInvoke不适用于C++。

+2

它比这更复杂 - 如果他的P/Invoke签名的返回类型是“string”,那么CLR将尝试释放该函数返回的内存,就像它是一个“BSTR”一样。仅仅改变原生返回类型不是解决方案。 – ildjarn

+0

事实上,正确的C方法是将一个预先分配的缓冲区及其大小一起传递给该函数,然后让它写入缓冲区供以后读取。不要让C为你做任何内存分配,这是坏的,坏消息。 – Blindy

+0

@ildjarn是正确的,Jerry Coffin关于stdcall的观点显示了另一个问题,但这里最大的问题仍然是尝试返回一个C++对象。 – hamstergene

4

这里的问题是,您正在使用STL string类,而C#不知道如何编组。您有两种选择:

  1. 重构您的C++代码以使用char *缓冲区。或写一个包装或超载或使用char *而不是string
  2. 在你的C++函数中编写一个C++/CLI包装,使用System::String并在内部调用STL字符串版本。
5

正如我相信你已经知道,C++ std::string实际上是一个模板。只有当模板被实例化时(如在这种情况下为std::basic_string<char>),该类型的对象和方法的签名的确切布局由C++编译器确定。

不幸的是,只有有问题的C++编译器有权访问所有相关信息(如模板源代码)来做出这些类型的决定。这就是为什么诸如模板之类的非C特性即使在不同的C++编译器之间通常也不“可转移”的原因,更不用说C++和C#了。

此外,C++名称通常以C++编译器特定的方式“破坏”。

你必须改变你的本地方法的签名,因此它成为一个“纯粹”的C函数:

  • 确保没有C++的名字被宣布功能extern "C"压延(你是已经这样做了)。使用char*参数代替std::string
  • 返回char*结果,而不是std::string,但是very careful你是怎么做到的。
  • 确保您DllImportAttribute.CallingConvention比赛的__cdecl__stdcall__fastcall在C.
+0

我已经试过,但我得到这个错误:尝试读取或写入受保护的内存。这通常表明其他内存已损坏。 – method

+0

@ user959615你能告诉我们你的代码吗? –

+0

当然,C#声明:static extern string encryption(string s); C++:extern“C”__declspec(dllexport)char * encryption(char * s) {return“test”; } – method