Tim
Tim

Reputation: 81

How can you Marshal a byte array in C#?

I'm trying to call the following C++ function that's wrapped up into a DLL:

unsigned char * rectifyImage(unsigned char *pimg, int rows, int cols)

My import statement looks like the following:

[DllImport("mex_rectify_image.dll")]
unsafe public static extern IntPtr rectifyImage(
byte[] data, int rows, int columns);

And my call routine looks like the following:

byte[] imageData = new byte[img.Height * img.Width * 3];
// ... populate imageData
IntPtr rectifiedImagePtr = rectifyImage(imageData, img.Height, img.Width);
Byte[] rectifiedImage = new Byte[img.Width * img.Height * 3];
Marshal.Copy(rectifiedImagePtr, rectifiedImage, 0, 3 * img.Width * img.Height);

However, I keep getting a runtime error:

A first chance exception of type System.AccessViolationException occurred in xxx.dll Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

I'm just wondering if the fault lies in the way I'm marshaling my data or in my imported DLL file... anyone have any ideas?

Upvotes: 8

Views: 14695

Answers (4)

Igor Levicki
Igor Levicki

Reputation: 1613

unsigned char *pimg can't be marshalled to byte[] automatically.

In order to pass the byte[] imageData you need to marshal it manually to IntPtr and pass said IntPtr to rectifyImage.

This is the recommended pattern for implementing such marshaling:

[DllImport("mex_rectify_image.dll", EntryPoint = "rectifyImage")]
private static extern IntPtr rectifyImageInternal(IntPtr data, int rows, int cols);

public static byte[] rectifyImage(byte[] data, int rows, int cols)
{
    IntPtr dataPtr = IntPtr.Zero;
    byte[] result = new byte[data.Length]; // assumes result is the same size as data
    
    try {
        dataPtr = Marshal.AllocHGlobal(data.Length);
        
        Marshal.Copy(data, 0, dataPtr, data.Length);
        
        IntPtr resultPtr = rectifyImageInternal(dataPtr, rows, cols);
        
        Marshal.Copy(resultPtr, result, 0, result.Length);
        
        // TODO:
        //  - Is resultPtr perhaps the same as dataPtr (in-place transform on source image)?
        //    If not:
        //      - Who is responsible for freeing resultPtr?
        //      - What allocation method was used for resultPtr (global/cotask/etc)?
        //  - Error handling
    } finally {
        Marshal.FreeHGlobal(dataPtr);
    }
    
    return Result;
}

Upvotes: 0

binhex213
binhex213

Reputation: 19

rectifyImage Is looking for a ponter to a the 1st byte in a block for data you are sending in the block. Try imageData[0]

Upvotes: -1

morishuz
morishuz

Reputation: 2402

not sure if this is your problem, but in general C++ pointers map to IntPtr. so try modifying your import statement to this:

[DllImport("mex_rectify_image.dll")]
unsafe public static extern IntPtr rectifyImage(
IntPtr pData, int rows, int columns);

Upvotes: 1

Tergiver
Tergiver

Reputation: 14517

This is likely occurring because the calling convention of the method is not as the marshaller is guessing it to be. You can specify the convention in the DllImport attribute.

You don't need the 'unsafe' keyword in the C# declaration here as it's not 'unsafe' code. Perhaps you were trying it with a 'fixed' pointer at one point and forgot to remove the unsafe keyword before posting?

Upvotes: 1

Related Questions