los*_*eep 3 c# c++ pinvoke winapi
我有一个C++函数导出为api像这样:
#define WIN322_API __declspec(dllexport)
WIN322_API char* Test(LPSTR str);
WIN322_API char* Test(LPSTR str)
{
return "hello";
}
Run Code Online (Sandbox Code Playgroud)
该函数由.DEF文件正确导出为API,因为我可以在Dependency Walker工具中看到它.现在我有一个C#测试程序:
[DllImport("c:\\win322.dll")]
public static extern string Test([MarshalAs(UnmanagedType.LPStr)] String str);
private void Form1_Load(object sender, EventArgs e)
{
string _str = "0221";
Test(_str); // runtime error here!
}
Run Code Online (Sandbox Code Playgroud)
在调用Test()方法时我得到错误:
"调用PInvoke函数'MyClient!MyClient.Form1 :: Test'使堆栈失衡.这很可能是因为托管PInvoke签名与非托管目标签名不匹配.请检查PInvoke签名的调用约定和参数是否与目标非托管签名."
我尝试了很多其他数据类型和编组,但什么也没得到!请帮助我!
它是由调用约定不匹配引起的,[DllImport]的默认值是Stdcall,但C编译器的默认值是Cdecl.在声明中使用CallingConvention属性.
这不是唯一的问题,这个代码会在Vista和Win7上崩溃.从C函数返回一个字符串非常麻烦,存在内存管理问题.目前尚不清楚谁负责释放字符串缓冲区.你现在正在返回一个文字,但这很快就会停止有用.下一站是使用malloc()作为返回字符串,其意图是调用者调用free().这不起作用,pinvoke marshaller无法调用它,因为它不知道C代码使用的是什么堆.
它将调用Marshal.FreeCoTaskMem().这是错的,字符串不是由CoTaskMemAlloc()分配的.这在XP及更早版本中没有被注意到,除了这很难诊断内存泄漏.在Vista和Win7上使用kaboom,他们有更严格的内存管理器.
你需要像这样重写C函数:
extern "C" __declspec(dllexport)
void __stdcall Test(const char* input, char* output, int outLen);
Run Code Online (Sandbox Code Playgroud)
现在调用者通过输出参数提供缓冲区,不再猜测谁拥有内存.您在C#声明中使用StringBuilder.
[DllImport("foo.dll")]
private static extern void Test(string input, StringBuilder output, int outLen);
...
var sb = new StringBuilder(666);
test("bar", sb, sb.Capacity);
string result = sb.ToString();
Run Code Online (Sandbox Code Playgroud)
注意在C代码中使用outLen参数,以确保不会溢出缓冲区.这会破坏垃圾收集堆,使应用程序崩溃,导致致命执行引擎错误.
| 归档时间: |
|
| 查看次数: |
1156 次 |
| 最近记录: |