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

时间:2009-10-16 17:50:45

标签: c# c interop pinvoke

我有一个带有签名的C API:

int GetBuffer(char* buffer, int* maxSize)

在C中,我会这样称呼它:

char buffer[4096];
int maxSize = 4096;
GetBuffer(buffer, &maxSize);

maxSize设置为缓冲区大小,并设置为填充的实际大小。

我需要从C#调用它。如何在“安全模式”下执行此操作?

5 个答案:

答案 0 :(得分:5)

一个选项只是使用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);

答案 1 :(得分: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 :(得分:2)

指针的句柄根本不适合“安全模式”模型。如果资源不是由Framework管理的,则它是不安全的。

答案 3 :(得分:2)

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

答案 4 :(得分:0)

一个简单而安全的选择是创建一个简单的类来包装该值或任何值,如以下代码:

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

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