5

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,

5 Answers 5

3

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);
    }
}
Sign up to request clarification or add additional context in comments.

Comments

3

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

3 Comments

There is no conversion from byte* to byte[], this code won't even compile. If he had control over the C# source that would be one thing, but it doesn't sound like he does from the original question.
That said, upon reading the question again it does sound like the OP has access to the C++ library as well, so you should simply suggest he add his own DllImport and change the signature to accept a byte* instead of a byte[], then this would work. Removing downvote.
" 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." So they have control over the C# and are writing a wrapper for a c++ library!
2

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

Comments

0

As easy as this:

ReadFromDevice(int deviceAddress, int numBytes, ref byte data);

...


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

5 Comments

But you changed the declaration of ReadFromDevice(). I don't have that code; that comes fromthe 3rd party library.
if it is .net you always have the code xD. This looks like a dllimport just turn on reflector and copy the 2 lines into your app.
The third-party library has supplied both the C++ compile file ( obviously cannot be changed ) and a C# wrapper class. Please see my answer for an obvious solution.
I am not following. Here's the Reflector output: public int ReadFromDevice(int deviceAddress, int numBytes, byte[] data) { return libDevicePINVOKE.ReadFromDevice(this.handle, deviceAddress, numbytes, data); } Then what?
than read out the handle (if there is not other way using reflection) and decompile libDevicePINVOKE.ReadFromDevice
0

The solution to this problem seems sort of obvious.

While it is true you could simply make your own wrapper class and DllImport and write your own wrapper class the solution is easier then that.

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

In the context of your post data is an output paramater. So the solution would be to send it an empty Byte array of the appropriate size then place filled array into your existing array.

All you have to do is determine the size of the returned array, which you knew before hand, then fill and replace.

I will just flat out and tell you that your idea of using an existing array would likely only work if you sent the address of the specfic element within the array ( which certianly is possible ) but your limited to what the C++ library actually expects which likely is just the address of the Byte array itself.

1 Comment

The point was to avoid those copy ops, since they are costly (in terms of time. This application processes over 30MB/sec of data). If we do it the way you described, then, for every data we read from the external device, the 3rd party API will copy the data to the small array (the 3rd argument in ReadFromDevice call), and then, we will have to do another copy operation to get the data in the small array written to our large array. So for every 30MBs of data read from the external device, there will be 60MBs of data movement. We want to write the data once, for performance reasons.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.