I have a C API with the signature:
int GetBuffer(char* buffer, int* maxSize)
在C语言中,我会这样调用它:
char buffer[4096];
int maxSize = 4096;
GetBuffer(buffer, &maxSize);
maxSize被设置为缓冲区大小,并且使用填充的实际大小进行设置。
我需要从C#中调用它。在“安全模式”下,我该怎么做?
一种选择是使用C#指针类型 - 这需要在方法/类上使用unsafe
块(或修饰符),并使用/unsafe
进行编译:
[DllImport(...)]
static extern int GetBuffer(byte* buffer, ref int maxSize);
缓冲区可以通过几种不同的方式进行分配。一种方法是使用固定的堆数组:
fixed (byte* buffer = new byte[4096])
{
int maxSize = buffer.Length;
GetBuffer(buffer, ref maxSize);
}
另一种方法是使用stackalloc
,但这仅适用于小缓冲区:
byte* buffer = stackalloc byte[4096];
int maxSize = 4096;
GetBuffer(buffer, ref maxSize);
这种方法在性能和分配模式方面与你的C代码几乎完全相同。
另一种选择是使用编组来处理堆数组,从而完全避免使用指针。
[DllImport(...)]
static extern int GetBuffer([Out] byte[] buffer, ref int maxSize);
byte[] buffer = new byte[4096];
int maxSize = buffer.Length;
GetBuffer(buffer, ref maxSize);
[DllImport("Kernel32.dll", SetLastError=true)]
static extern Int32 GetBuffer(byte[] buffer,ref Int32 maxSize);
并且使用它:
byte[] myBuf = new myBuf[4096];
Int32 maxSize = myBuf.Length;
GetBuffer(myBuf, ref maxSize);
这应该可以在不使用不安全代码的情况下工作。
extern int GetBuffer(IntPtr buffer, ref int bufSize);
// ...
byte[] buf = new byte[kBufSize];
GCHandle handle = GCHandle.Alloc(buf, GCHandleType.Pinned); // possibly expensive call
IntPtr p = handle.AddrOfPinnedObject();
int size = buf.Length;
int ret = GetBuffer(p, ref size);
handle.Free();
拥有指针的句柄完全不符合“安全模式”模型;如果资源未由框架管理,则它是不安全的。
public class Value<T> where T: struct
{
public static implicit operator T(Value<T> val)
{
return val.Value;
}
private T _value;
public Value(T value)
{
_value = value;
}
public Value() : this(default)
{
}
public T Value
{
get
{
return _value;
}
set
{
_value = value;
}
}
public override string ToString()
{
return _value.ToString();
}
}
char
在大多数情况下技术上是sbyte
,但通常你最终会将它们作为byte
来处理。我在上面的代码中将其更改为byte
。 - Sam Harwell