dic*_*ice 2 c# pinvoke stringbuilder
我想要一个干净的方法来增加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);
}
}
Run Code Online (Sandbox Code Playgroud)
本地标题
#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);
Run Code Online (Sandbox Code Playgroud)
本机代码
#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");
}
Run Code Online (Sandbox Code Playgroud)
我有一个理论为什么会发生这种情况.我怀疑编组StringBuilder
涉及制作数据的副本,将其传递给P/Invoke调用,然后复制回StringBuilder
.我实际上无法验证这一点.
对此的唯一替代方法是首先要求展StringBuilder
平(它在内部是一个链接的列表char[]
)和char[]
固定的,即使这样,这只能用于编组指针到Unicode-chars字符串,但不能ANSI或COM字符串.
因此,当您传递一个StringBuilder
参数时,.NET有一个显而易见的地方可以复制任何更改:在P/Invoke返回之后.
当你传递一个委托返回一个时,情况也是如此StringBuilder
.在这种情况下,.NET需要创建一个将int => StringBuilder
函数转换为函数的包装器int => char*
.这个包装器将创建char*
缓冲区并填充它,但显然无法复制任何更改.这还不能说该功能后,做到这一点需要委托回报:它仍然为时过早!
实际上,在反向拷贝可能发生的地方根本没有明显的位置.
所以我的猜测是发生了这样的事情:当编组一个StringBuilder
返回委托时,.NET只能执行单向转换,因此你所做的任何更改都不会反映在StringBuilder
.这比完全无法组织这样的代表要好一些.
至于解决方案:我建议首先询问本机代码缓冲区需要多大,然后在第二次调用中传递适当大小的缓冲区.或者,如果您需要更好的性能,请猜测足够大的缓冲区,但允许本机方法进行通信,以确保需要更多空间.这样,大多数调用只涉及一个P/Invoke转换.
这可以包装成一个更方便的功能,您可以从托管世界调用,而无需担心缓冲区.