如何在C#中传递一个指向整数的指针?

8

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#中调用它。在“安全模式”下,我该怎么做?

5个回答

6

一种选择是使用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);

你也可以通过分配数组并使用 GCHandle 获取指向第一个元素的指针(http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.gchandle.aspx)来避免不安全的代码。 - plinth
在C语言中,char在大多数情况下技术上sbyte,但通常你最终会将它们作为byte来处理。我在上面的代码中将其更改为byte - Sam Harwell
谢谢,这确实是我的错误(我不知道有哪个平台可以获得.NET,而且C的“char”与C#的“byte”不匹配)。 - Pavel Minaev

3
你需要使用所谓的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);

3

这应该可以在不使用不安全代码的情况下工作。

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();

2

拥有指针的句柄完全不符合“安全模式”模型;如果资源未由框架管理,则它是不安全的。


-1
一个简单而安全的选项是创建一个简单的类来包装值,或者像以下代码一样的任何值:
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(); 
    }
} 

将该类的实例传递而不是值本身,几乎就像使用指针一样。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接