我是与非托管库交互的新手。我有一个非托管的C函数,它通过函数内的引用修改字符串。我在从C#传递字符串并通过C函数修改它时遇到了麻烦。
这是C函数:
__declspec(dllexport) void __stdcall Test(char* name)
{
*name = "Bar";
}
这是C#DLL导入代码:
[DllImport(@"C:/blah/mylibrary.dll")]
public extern static string Test(string name);
这是我用来调用函数的代码:
string s = "foo";
Test(s);
//I want s to be "Bar" after the above line
我尝试在字符串参数上使用“ref”和“out”,并尝试将编组作为LPStr。根据我的尝试,我得到像
这样的错误“作为String传入的指针不能位于进程地址空间的底部64K中。”
或
“尝试读取或写入受保护的内存。这通常表示其他内存已损坏。”
我确定我只是用指针做一些愚蠢的事情。有人可以帮我确定合适的C#代码,使“s”等于“bar”吗?
谢谢
答案 0 :(得分:12)
您的C Test
函数没有像您说的那样执行任何操作。它只需要一个局部变量(name
)并将其分配给一个固定的字符串。为了做你所说的,它必须在name
指向的地址中进行复制操作:
__declspec(dllexport) void __stdcall Test(char* name)
{
strcpy(name, "Bar");
}
当然,由于你的函数签名不正确(没有指定缓冲区长度),这样的操作在等待中是一种灾难。
考虑到C函数如上所述,那么您应该遵循Default Marshaling for Strings中指定的规则:
在某些情况下,固定长度 必须传入字符缓冲区 要操纵的非托管代码。 简单地传递字符串不起作用 在这种情况下,因为被叫方不能 修改传递的内容 缓冲。即使字符串被传递 通过引用,没有办法 将缓冲区初始化为给定大小。
解决方案是通过 StringBuilder缓冲区作为参数 而不是一个字符串。一个StringBuilder 可以被解除引用和修改 被叫者,只要没有 超过了容量 StringBuilder的。它也可以 初始化为固定长度。对于 例如,如果你初始化一个 StringBuilder缓冲区的容量为 N,编组提供缓冲区 大小(N + 1)个字符。 +1帐户 对于非托管字符串的事实 有一个null终止符 StringBuilder没有。
所以你的DLL应该是这样的:
[DllImport(@"C:/blah/mylibrary.dll")]
public extern static string Test(StringBuilder name);
并通过传递正确大小的StringBuilder
:
StringBuilder foo = new StringBuilder(256);
Test(foo);
如果添加长度参数,则会在C接口中添加一些健全性。