How can I pass a pointer to an integer in C#
Asked Answered
A

5

9

I have a C API with the signature:

int GetBuffer(char* buffer, int* maxSize)

In C, I will call it this way:

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

maxSize is set to the buffer size, and set with the actual size filled.

I need to call it from C#. How do I do that under "safe mode"?

Allo answered 16/10, 2009 at 17:50 Comment(0)
P
7

One option is simply to use C# pointer types - this requires unsafe block (or modifier on method/class), and compiling with /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:

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.

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);
Persson answered 16/10, 2009 at 18:0 Comment(3)
You can also avoid the unsafe code by allocating your array then using a GCHandle to get a pointer to the first element (msdn.microsoft.com/en-us/library/…).Rhiannonrhianon
char in C is technically sbyte in most cases, but normally you end up working with them as byte. I changed it to byte in the code above.Episode
Thanks, it was indeed my mistake (and I don't know of any platform on which you can get .NET, and C char woudn't match C# byte).Persson
R
3

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#.

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);
Radiotransparent answered 16/10, 2009 at 18:0 Comment(0)
R
3

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();
Rhiannonrhianon answered 16/10, 2009 at 18:8 Comment(0)
A
2

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.

Aluino answered 16/10, 2009 at 17:52 Comment(0)
F
-1

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.

Fridafriday answered 12/2, 2021 at 22:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.