如何通过引用修改该字符串的非托管 C 库来发送字符串? [英] How to send a string by reference to an unmanaged C library that modifies that string?

查看:43
本文介绍了如何通过引用修改该字符串的非托管 C 库来发送字符串?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我刚接触与非托管库交互的世界.我有一个非托管 C 函数,它通过函数内的引用修改字符串.我在从 C# 传递字符串并让它被 C 函数修改时遇到问题.

I am new to the world of interacting with unmanaged libraries. I have an unmanaged C function that modifies a string by reference within the function. I'm having trouble passing a string from C# and getting it modified by the C function.

这是 C 函数:

__declspec(dllexport) void __stdcall Test(char* name)
{
    *name = "Bar";
}

这是 C# DLL 导入代码:

This is the C# DLL import code:

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

这是我用来调用函数的代码:

This is the code I'm using to call the function:

string s = "foo";
Test(s);
//I want s to be "Bar" after the above line

我尝试在字符串参数上使用ref"和out",并尝试将编组作为 LPStr.根据我的尝试,我要么得到像

I have tried using "ref" and "out" on the string parameter, and tried Marshalling as an LPStr. Depending on what I try, I either get an error like

作为字符串传入的指针不得位于进程地址空间的底部 64K."

"The pointer passed in as a String must not be in the bottom 64K of the process's address space."

尝试读取或写入受保护的内存.这通常表明其他内存已损坏."

"Attempted to read or write protected memory. This is often an indication that other memory is corrupt."

我确定我只是在用我的指针做一些愚蠢的事情.有人可以帮我确定合适的 C# 代码来让s"等于bar"吗?

I'm sure I'm just doing something stupid with my pointers. Can someone help me determine the appropriate C# code to get "s" to equal "bar"?

谢谢

推荐答案

你的 C Test 函数没有像你说的那样做任何事情.它所做的一切都需要一个局部变量(name)并将其分配给一个固定的字符串.要执行您所说的操作,它必须对 name 指向的地址执行 copy 操作:

Your C Test function doesn't do anything like you said it does. All it does it takes a local variable (name) and assigns it to a fixed string. To do what you said it does it would had to do a copy operation into the address pointed to by name:

__declspec(dllexport) void __stdcall Test(char* name)
{
    strcpy(name, "Bar");
}

当然,这样的操作是等待的灾难,因为您的函数签名不正确(未指定缓冲区长度).

Of course, such an operation is a disaster in waiting since you have incorrect function signature (buffer lengths are not specified).

考虑到 C 函数如上,那么您应该遵循 字符串的默认封送处理:

Considering that the C function is as above, then you should follow the rules specified at Default Marshaling for Strings:

在某些情况下,一个固定长度的字符缓冲区必须传入要操作的非托管代码.简单地传递一个字符串是行不通的在这种情况下,因为被叫方不能修改传递的内容缓冲.即使字符串被传递通过引用,没有办法将缓冲区初始化为给定的大小.

In some circumstances, a fixed-length character buffer must be passed into unmanaged code to be manipulated. Simply passing a string does not work in this case because the callee cannot modify the contents of the passed buffer. Even if the string is passed by reference, there is no way to initialize the buffer to a given size.

解决方案是通过一个StringBuilder 缓冲区作为参数而不是字符串.一个字符串生成器可以取消引用和修改被叫方,前提是它没有超出容量字符串生成器.它也可以初始化为固定长度.为了例如,如果您初始化一个StringBuilder 缓冲区的容量为N,封送拆收器提供了一个缓冲区大小 (N+1) 个字符.+1 帐户因为非托管字符串有一个空终止符而StringBuilder 没有.

The solution is to pass a StringBuilder buffer as the argument instead of a string. A StringBuilder can be dereferenced and modified by the callee, provided it does not exceed the capacity of the StringBuilder. It can also be initialized to a fixed length. For example, if you initialize a StringBuilder buffer to a capacity of N, the marshaler provides a buffer of size (N+1) characters. The +1 accounts for the fact that the unmanaged string has a null terminator while StringBuilder does not.

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

So your DLL should be like this:

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

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

and call it by passing a properly sized StringBuilder:

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

如果添加长度参数,C 接口会增加一些理智.

Some sanity would be added to the C interface if you add a length parameter.

这篇关于如何通过引用修改该字符串的非托管 C 库来发送字符串?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆