2

I have a large char[] buffer allocated in c# and want to pass a pointer to this data to an umnanaged c function in a DLL.

Now i think for this to work the char buffer must be FIXED so that GC cant shift it around while the function is working.

If so, would i declare the buffer FIXED and call the DLL within and UNSAFE block ??

fixed ( int* p = &bigbuffer )
{
    processbigbuffer(bigbuffer); 
}

I have searched but not many references highlight the issue that the memory belongs to c# and how this would work in a DLL.

Thanks

Kelsey Bowman
  • 125
  • 1
  • 2
  • 8

2 Answers2

2

You don't have to use fixed. The runtime will do that for you automatically. So if you have the C function:

void ProcessBigBuffer(char* buffer);

Then your managed prototype would look like:

[DllImport("foo.dll", CallingConvention=CallingConvention.Cdecl)]
static extern void ProcessBigBuffer([MarshalAs(UnmanagedType.LPArray)] char[] buffer);

And your call site would be:

char[] buffer = new char[bufferSize];
// initialize the buffer
// and then process it
ProcessBigBuffer(buffer);

See also:

Jim Mischel
  • 131,090
  • 20
  • 188
  • 351
  • If you don't mind an add on question. In the line static extern void ProcessBigBuffer([MarshalAs UnmanagedType.LPArray] buffer); why is there an actual object in there (buffer). It seems that if this is a function prototype only the types need to be declared. I don't doubt your answer, i have seen this many times before, just curious. Does it mean i can only call the function with a variable named 'buffer' – Kelsey Bowman May 17 '13 at 15:05
  • @KelseyBowman: The C# specification requires that you provide a name for the parameter. That's just a formal name. You can call the buffer anything you like at the call site. That is, you could write `ProcessBigBuffer(bigArrayOfInts);`. You see the same kind of thing in C# when you declare abstract methods. See http://msdn.microsoft.com/en-us/library/aa664435(v=vs.71).aspx – Jim Mischel May 17 '13 at 15:20
  • Is there a way to do this without creating a copy of the buffer?? – Kelsey Bowman May 17 '13 at 16:45
  • @280Z28: Are you sure of that? The accepted answer here: http://stackoverflow.com/q/2218444/56778 indicates otherwise, and has an MSDN link that appears to say that the array will be pinned. – Jim Mischel May 17 '13 at 21:39
  • @JimMischel I was mistaken. The behavior I described does not apply with the array element type is a primitive type or an `IntPtr`/`UIntPtr`. It *does* apply for user-defined value types, even if the value type is as simple as `struct Foo { int value; }`, where the argument to the native code is `Foo[]`. – Sam Harwell May 17 '13 at 22:24
  • @JimMischel (rewrote the previous comment to correct errors) I edited the answer to fix the syntax errors. One thing to note is this only behaves the same way as the `fixed` keyword for primitive arrays (`int[]`, `char[]`, etc.). For user-defined structures, the marshaler will allocate a fresh block of memory to hold the array data, copy the data to the new allocation, and then free the result when the native method returns. If you also specify the `[Out]` attribute on the parameter, then before freeing the memory created by the marshaler it will copy the data back to the managed array. – Sam Harwell May 17 '13 at 22:46
  • @KelseyBowman: Please see the comments. The answer I gave is correct in your situation of passing an array of `char`. – Jim Mischel May 17 '13 at 22:48
  • Thanks Jim it worked. Its nice to get the speed of raw C and pointers when you want them. – Kelsey Bowman May 20 '13 at 14:09
1

Yes, indeed. You can do this without unsafe code and without copying the data.

var chars = new char[LARGE_AMOUNT];

var handle = GCHandle.Alloc(chars);
try
{ 
    processBigBuffer(/* this is an IntPtr -> */ handle.AddrOfPinnedObject, ... );
}
finally
{
    handle.Free();
}

My OpenCL bindings use this all over the place (and are very performant).

Ani
  • 10,826
  • 3
  • 27
  • 46