我有一个带有签名的C API:
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);
这应该没有不安全的代码。
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();
拥有指针的句柄根本不适合“安全模式”模型;如果资源不是由框架管理的,那么它是不安全的。
您需要使用所谓的P\Invoke,并从C#中生成引用Dll中C函数的函数声明。
但是,在将缓冲区传入/传出非托管代码时,必须非常小心。该框架会为您处理一些事情,但是您可能需要确保传递给非托管调用的内存不会被垃圾回收器移动。
[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);