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

发布于 2024-11-17 18:05:45 字数 738 浏览 4 评论 0原文

我是与非托管库交互的新手。我有一个非托管 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。根据我的尝试,我要么收到类似的错误

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

或者

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

我确信我只是用我的指点做了一些愚蠢的事情。有人可以帮助我确定适当的 C# 代码以使“s”等于“bar”吗?

谢谢

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.

Here's the C function:

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

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

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

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

or

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

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"?

Thank you

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

无声情话 2024-11-24 18:05:45

您的 C Test 函数没有执行您所说的任何操作。它所做的只是获取一个局部变量(name)并将其分配给一个固定字符串。要执行您所说的操作,必须对 name 指向的地址执行复制操作:

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

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

考虑到 C 函数如上所示,那么您应该遵循 Default Marshaling 中指定的规则对于字符串

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

解决方案是通过
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 接口将添加一些理智。

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).

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.

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.

So your DLL should be like this:

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

and call it by passing a properly sized StringBuilder:

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

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文