如何通过引用发送字符串来修改该字符串的非托管C库?

时间:2011-06-29 00:11:22

标签: c# c unmanaged managed

我是与非托管库交互的新手。我有一个非托管的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”吗?

谢谢

1 个答案:

答案 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接口中添加一些健全性。