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

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

谢谢

你的C Testfunction没有像你说的那样做。 它只需要一个局部变量( 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表示非托管字符串具有空终止符而StringBuilder不具有空终止符。

所以你的DLL应该是这样的:

 [DllImport(@"C:/blah/mylibrary.dll")] public extern static string Test(StringBuilder name); 

并通过传递适当大小的StringBuilder调用它:

 StringBuilder foo = new StringBuilder(256); Test(foo); 

如果添加长度参数,则会在C接口中添加一些完整性。