简体   繁体   English

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

[英]How can I pass a pointer to an integer in C#

I have a C API with the signature:我有一个带有签名的 C API:

int GetBuffer(char* buffer, int* maxSize)

In C, I will call it this way:在 C 中,我会这样称呼它:

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

maxSize is set to the buffer size, and set with the actual size filled. maxSize 设置为缓冲区大小,并设置为填充的实际大小。

I need to call it from C#.我需要从 C# 调用它。 How do I do that under "safe mode"?我如何在“安全模式”下做到这一点?

One option is simply to use C# pointer types - this requires unsafe block (or modifier on method/class), and compiling with /unsafe :一种选择是简单地使用 C# 指针类型 - 这需要unsafe块(或方法/类上的修饰符),并使用/unsafe编译:

[DllImport(...)]
static extern int GetBuffer(byte* buffer, ref int maxSize);

Buffer can be allocated in several different ways.缓冲区可以通过几种不同的方式分配。 One would be to use a pinned heap array:一种是使用固定堆数组:

fixed (byte* buffer = new byte[4096])
{
    int maxSize = buffer.Length;
    GetBuffer(buffer, ref maxSize);
}

Another is to use stackalloc , though this is only feasible for small buffers:另一种是使用stackalloc ,尽管这仅适用于小缓冲区:

byte* buffer = stackalloc byte[4096];
int maxSize = 4096;
GetBuffer(buffer, ref maxSize);

This particular approach is virtually identical to your C code in terms of performance and allocation patterns.这种特殊方法在性能和分配模式方面几乎与您的 C 代码相同。

Another option altogether is to use marshaling for heap arrays, and avoid pointers entirely.另一种选择是对堆数组使用封送处理,并完全避免使用指针。

[DllImport(...)]
static extern int GetBuffer([Out] byte[] buffer, ref int maxSize);

byte[] buffer = new byte[4096];
int maxSize = buffer.Length;
GetBuffer(buffer, ref maxSize);

Having a handle to the pointer doesn't fit the "safe mode" model at all;拥有指针的句柄根本不适合“安全模式”模型; if the resource isn't managed by the Framework, it is unsafe.如果资源不是由框架管理的,则它是不安全的。

This should work without unsafe code.这应该可以在没有不安全代码的情况下工作。

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

You need to use what is called P\\Invoke , and generate a function declaration that to reference the C function in the Dll from C#.您需要使用所谓的P\\Invoke ,并生成一个函数声明,以从 C# 引用 Dll 中的 C 函数。

However, you have to be very careful when passing buffers in/out of unmanaged code.但是,在将缓冲区传入/传出非托管代码时必须非常小心。 The framework will take care of some things for you but you may need to ensure that memory that you pass into the unmanaged call doesn't get moved by the Garbage collector.该框架将为您处理一些事情,但您可能需要确保传递给非托管调用的内存不会被垃圾收集器移动。

[DllImport("Kernel32.dll", SetLastError=true)]
static extern Int32 GetBuffer(byte[] buffer,ref Int32 maxSize);

And to use it:并使用它:

byte[] myBuf = new myBuf[4096];
Int32 maxSize = myBuf.Length;

GetBuffer(myBuf, ref maxSize);

One easy and safe option is to create a simple class that wraps the value, or any value like the following code:一个简单而安全的选择是创建一个简单的类来包装该值,或任何类似以下代码的值:

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

Passing on instances of this class instead of the value itself works almost like working with pointers.传递此类的实例而不是值本身几乎就像使用指针一样。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM