从 C# 调用非托管函数:我应该传递 StringBuilder 还是使用不安全代码?

发布于 2024-08-09 18:40:04 字数 1060 浏览 7 评论 0原文

我有一个 C# 程序,需要将字符缓冲区传递给非托管函数。我发现了两种似乎可靠的方法,但我不确定应该选择哪一种。

这是非托管函数的签名。

extern "C" __declspec(dllexport) int getNextResponse(char *buffer);

第一个选项是将缓冲区定义为 StringBuilder,如下所示。

//at class level...
[DllImport("mydll.dll")]
static extern int getNextResponse(StringBuilder buffer);

//in main method body...
StringBuilder sb = new StringBuilder(" ", 65536);
int rc = getNextResponse(sb);

这很简单,而且有效,我想我基本上理解它为什么有效,因为 StringBuilder 在幕后有一个缓冲区,所以(我假设)互操作层只是将 StringBuilder 编组到 char * 。

另一种选择是使用不安全的代码。

//at class level...
[DllImport("mydll.dll")]
static extern int getNextResponse(byte* buffer);

//separate method...
private static unsafe int runGetNextResponse(byte[] buffer)
{
    fixed (byte* p = buffer)
    {
        int rc = getNextResponse(p);
        return rc;
    }            
}

//in main method body...
byte[] b = new byte[65536];
int rc = runGetNextResponse(b);

第二种方法是更多的代码,但它也更明确地说明正在发生的事情。

这两种方法基本上做同样的事情吗?有什么理由选择其中之一而不是其他吗?

I've got a C# program that needs to pass a char buffer to an unmanaged function. I've found two ways that seem to work reliably, but I'm not sure which I should choose.

Here's the unmanaged function's signature.

extern "C" __declspec(dllexport) int getNextResponse(char *buffer);

The first option is to define the buffer as a StringBuilder, as follows.

//at class level...
[DllImport("mydll.dll")]
static extern int getNextResponse(StringBuilder buffer);

//in main method body...
StringBuilder sb = new StringBuilder(" ", 65536);
int rc = getNextResponse(sb);

This is simple, and it works, and I think I basically understand why it works because the StringBuilder has a buffer behind the scenes, so (I assume) the interop layer is just marshalling the StringBuilder to a char *.

The other option is using unsafe code.

//at class level...
[DllImport("mydll.dll")]
static extern int getNextResponse(byte* buffer);

//separate method...
private static unsafe int runGetNextResponse(byte[] buffer)
{
    fixed (byte* p = buffer)
    {
        int rc = getNextResponse(p);
        return rc;
    }            
}

//in main method body...
byte[] b = new byte[65536];
int rc = runGetNextResponse(b);

The second approach is more code, but it's also more explicit about what's going on.

Are these two approaches doing basically the same thing? Is there any reason to choose one over the other?

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

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

发布评论

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

评论(5

梦中的蝴蝶 2024-08-16 18:40:04

我非常喜欢使用 StringBuilder 版本。

两者之间不会有很大的差异,并且使用不安全的代码也不太干净。

在我看来,既然有一种方法可以使用核心库类来解决问题,那么在没有明确(和需要)好处的情况下使用不安全的代码是一种过早的优化。

I'd strongly prefer using the StringBuilder version.

There's not going to be a huge difference between the two, and using unsafe code is not nearly as clean.

In my opinion, since there is a way to solve the problem using a core library class, using unsafe code without a clear (and needed) benefit is a premature optimization.

深者入戏 2024-08-16 18:40:04

虽然首选使用 StringBuilder,但有一个警告。例如,想象一下,在您的 getNextResponse 方法中,您存储了指向某个静态变量的指针,并在另一个方法中使用它:

char* globalPointer;

int getNextResponse(char *buffer) {
    globalPointer = buffer;
    return 0;
}

void someOtherMethod() {
    printf("%s\n", globalPointer);
}

现在让我们看看托管端:

var sb = new StringBuilder();
sb.Append("Hello World");
int result = getNextResponse(sb);
Console.WriteLine(result);
someOtherMethod(); // kaboom: The GC could have already destroyed the string builder.

不安全的方法保证您的内存位置不会被移动:

byte[] buffer = Encoding.UTF8.GetBytes("Hello World");
fixed (byte* p = buffer)
{
    int result = getNextResponse(p);
    Console.WriteLine(result);
    someOtherMethod(); // works fine as the buffer address is pinned down in memory
}

在这种情况下,不安全的版本会工作得更好。

While using a StringBuilder is preferred there's one caveat. Imagine for example that in your getNextResponse method you store the pointer to some static variable and use it in another method:

char* globalPointer;

int getNextResponse(char *buffer) {
    globalPointer = buffer;
    return 0;
}

void someOtherMethod() {
    printf("%s\n", globalPointer);
}

Now let's look at the managed side:

var sb = new StringBuilder();
sb.Append("Hello World");
int result = getNextResponse(sb);
Console.WriteLine(result);
someOtherMethod(); // kaboom: The GC could have already destroyed the string builder.

The unsafe method guarantees you that the memory location won't be moved around:

byte[] buffer = Encoding.UTF8.GetBytes("Hello World");
fixed (byte* p = buffer)
{
    int result = getNextResponse(p);
    Console.WriteLine(result);
    someOtherMethod(); // works fine as the buffer address is pinned down in memory
}

In this case the unsafe version will work better.

錯遇了你 2024-08-16 18:40:04

虽然我无法做出明确的评价,但我可以分享我自己的经历。我专门使用了 StringBuilder 方法,没有遇到任何问题。我喜欢它更简单的代码和避免不安全。

While I can't weigh in definitively, I can share my own experiences. I have used the StringBuilder method exclusively and have had no problems with it. I like the simpler code of it and the avoidance of unsafe.

流心雨 2024-08-16 18:40:04

这取决于编组成本。如果您进行了大量编组或编组的数据很大,您可能希望重用缓冲区,而不是每次都创建/销毁字符串生成器缓冲区。

It depends on the cost of marshalling. If you do a lot of marshalling or the data being marshalled is large, you may want to reuse the buffer instead of creating/destroying the string builder buffer every time.

为人所爱 2024-08-16 18:40:04

这取决于缓冲区中的数据实际是什么。如果是字符数据,请使用 StringBuilder 方法。如果是二进制数据,请使用字节数组,但不要使用不安全的方法。虽然对“不安全”的过度恐惧变得普遍有点愚蠢,并且没有理由在有必要时不使用它,但在这种情况下这是不必要的。使用:

//at class level...
[DllImport("mydll.dll")]
static extern int getNextResponse([In, Out] byte[] buffer);

//in main method body...
byte[] buffer = new byte[65536];
int rc = getNextResponse(buffer);

It depends on what the data in the buffer actually is. If it is character data use the StringBuilder approach. If it is binary data use a byte array instead, but don't use the unsafe method. While the exaggerated fear of 'unsafe' that is getting common is a little silly and there is no reason not to use it when warranted, in this case it is unnecessary. Use:

//at class level...
[DllImport("mydll.dll")]
static extern int getNextResponse([In, Out] byte[] buffer);

//in main method body...
byte[] buffer = new byte[65536];
int rc = getNextResponse(buffer);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文