Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PInvoke, pointers and array copy

We're building an application on c#, .net 4.0, on Win7 x64, targeting x32.

We are using a 3rd party library in our application. We understand that this library is written using C++. However, to let c# developers use this library, they've wrapped it using P/Invoke, so that's how we call the API functions.

One of the API calls is as follows:

ReadFromDevice(int deviceAddress, int numBytes, Byte[] data);

This function reads numBytes of data from an external device and places it in data[]. As you can see, it expects to see a C# Byte array as the 3rd argument. Now, our problem is, we would like to read data to an arbitrary location in a predeclared array. For example:

Byte[] myData = new Byte[1024*1024*16];
ReadFromDevice(0x100, 20000, &myData[350]) // Obviously not possible in C#

If we were using C/C++, this would be trivial. Given that the underlying API is written in C++, I feel that we should be able to do this in c# as well, however, I can't figure out how to do this in c#. Maybe we can somehow call the underlying library not through the supplied P/Invoke interface and write a custom interface?

Any ideas would be appreciated.

Regards,

like image 819
SomethingBetter Avatar asked Feb 04 '11 08:02

SomethingBetter


3 Answers

While the other answers here are close, none are quite complete.

First you simply need to declare your own p/invoke declaration. This is the sweet thing about p/invoke; There's never just one way to do it.

[DllImport("whatever.dll")]
unsafe extern static void ReadFromDevice(int deviceAddress, int numBytes, byte* data);

Now you can call it with

unsafe static void ReadFromDevice(int deviceAddress, byte[] data, int offset, int numBytes)
{
    fixed (byte* p = data)
    {
        ReadFromDevice(deviceAddress, numBytes, p + offset);
    }
}
like image 135
Tergiver Avatar answered Sep 20 '22 02:09

Tergiver


You can use pointers but requires to build with unsafe mode checked.

Byte[] myData = new Byte[1024*1024*16];
fixed( Byte * pB = &myData[350])
{
   ReadFromDevice(0x100, 20000,pB  ) 
} 

But first you need to change the exported method signature to.

ReadFromDevice(int deviceAddress, int numBytes, Byte * data);
like image 31
N4rk0 Avatar answered Sep 22 '22 02:09

N4rk0


You are still able to perform pointer arithmetic in c#.

If you re-declare you dll import (against the C++ library) to use an IntPtr to pass the byte array , this can be incremented by 350 using the Add method (This actually returns a new intptr)

There is an example of something similar here: http://msdn.microsoft.com/en-us/library/system.intptr.add.aspx#Y811

like image 36
Jimmy Avatar answered Sep 19 '22 02:09

Jimmy