gpt4 book ai didi

c# - 如何从 PInvoke native 回调返回 StringBuilder 或其他字符串缓冲区

转载 作者:行者123 更新时间:2023-12-02 10:59:26 25 4
gpt4 key购买 nike

我想要一种干净的方法来增加 native 代码填充所需的 StringBuilder() 的大小,下面的回调方法看起来很干净,但不知何故我们得到了缓冲区的副本而不是实际的缓冲区 - 我'我对解释和解决方案感兴趣(最好坚持回调类型分配,因为如果它可以工作的话,它会很好而且干净)。

using System;
using System.Runtime.InteropServices;
using System.Text;

namespace csharpapp
{
internal class Program
{
private static void Main(string[] args)
{
var buffer = new StringBuilder(12);
// straightforward, we can write to the buffer but unfortunately
// cannot adjust its size to whatever is required
Native.works(buffer, buffer.Capacity);
Console.WriteLine(buffer);

// try to allocate the size of the buffer in a callback - but now
// it seems only a copy of the buffer is passed to native code
Native.foo(size =>
{
buffer.Capacity = size;
buffer.Replace("works", "callback");
return buffer;
});
string s = buffer.ToString();
Console.WriteLine(s);
}
}

internal class Native
{
public delegate StringBuilder AllocateBufferDelegate(int bufsize);
[DllImport("w32.dll", CharSet = CharSet.Ansi)]
public static extern long foo(AllocateBufferDelegate callback);
[DllImport("w32.dll", CharSet = CharSet.Ansi)]
public static extern void works(StringBuilder buf, int bufsize);
}
}

原生 header

#ifdef W32_EXPORTS
#define W32_API __declspec(dllexport)
#else
#define W32_API __declspec(dllimport)
#endif

typedef char*(__stdcall *FnAllocStringBuilder)(int);
extern "C" W32_API long foo(FnAllocStringBuilder fpAllocate);
extern "C" W32_API void works(char *buf, int bufsize);

native 代码

#include "stdafx.h"
#include "w32.h"
#include <stdlib.h>

extern "C" W32_API long foo(FnAllocStringBuilder fpAllocate)
{
char *src = "foo X";
int len = strlen(src) + 1;

char *buf = fpAllocate(len);
return strcpy_s(buf,len,src);
}

extern "C" W32_API void works(char *buf, int bufsize)
{
strcpy_s(buf,bufsize,"works");
}

最佳答案

对于为什么会发生这种情况,我有一个理论。我怀疑 StringBuilder 的编码涉及制作数据副本,将其传递给 P/Invoke 调用,然后复制回 StringBuilderI couldn't actually verify this不过。

唯一的替代方案是需要首先展平 StringBuilder(它内部是 char[] 的链接列表),并且 char[] 固定,即使如此,这也仅适用于编码到指向 Unicode 字符字符串的指针,而不适用于 ANSI 或 COM 字符串。

因此,当您将 StringBuilder 作为参数传递时,.NET 有一个明显的位置可以将任何更改复制回来:就在 P/Invoke 返回之后。

当您传递返回 StringBuilder 的委托(delegate)时,情况并非如此。在这种情况下,.NET 需要创建一个包装器,将 int => StringBuilder 函数转换为 int => char* 函数。该包装器将创建 char* 缓冲区并填充它,但显然还无法复制任何更改。在接受委托(delegate)返回的函数之后,它也无法执行此操作:现在还为时过早!

事实上,根本没有明显的地方可以发生反向复制。

所以我的猜测是,发生的情况是这样的:当编码 StringBuilder 返回委托(delegate)时,.NET 只能执行单向转换,因此您所做的任何更改都不会反射(reflect)在StringBuilder。这比完全无法召集此类代表要好一些。

<小时/>

至于解决方案:我建议首先询问 native 代码缓冲区需要有多大,然后在第二次调用中传递适当大小的缓冲区。或者,如果您需要更好的性能,请猜测足够大的缓冲区,但允许 native 方法传达需要更多空间。这样,大多数调用将只涉及一次 P/Invoke 转换。

这可以封装到一个更方便的函数中,您可以从托管世界中调用该函数,而不必担心缓冲区。

关于c# - 如何从 PInvoke native 回调返回 StringBuilder 或其他字符串缓冲区,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9378626/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com