Nicola
Nicola

Reputation: 91

C# pointer on unmanaged data from C++ CLI project

I have a SDK written in C++ which manages a device. My program controlling the device is written in C#, so naturally a CLI wrapper class does the translation between both languages. My C# project includes the wrapper as a DLL.

My issue is that the C++ SDK is using pointers to head to arrays of data. These pointers are also available in the wrapper.

Wrapper .cpp code:

Wrapper::Wrapper()
{
    myData = new DataAquis(); //initiate C++ class's instance
}

int Wrapper::Start()
{
    //(..)
    int num = myData->Start();
    ptr = (myData->img);
    return num;
}

This code initializes the device and creates a pointer to a data structure (array of unsigned char).

Wrapper SDK .cpp code:

int DataAquis::Start()
{
    // (...)
    // Pointer from SDK
    img = pBuffer;
    // (...)
    return FAILED(nError) ? -1 : 0;
}

Wrapper .h code:

public ref class Wrapper
{
    public:
        Wrapper();

        // (...)
        unsigned char *ptr;

    private:
        // (...)
};

Code C#:

public static Wrapper myDataAqui;

// (...)

private static void DataAquisition()
{
    // call the start function in wrapper
    myDataAqui.Start();

    // Unsafe code for pointer use
    unsafe
    {
        // point to aquired data
        byte* imgptr1 = myDataAqui.ptr; 

        // AccesViolationException in above line. 

        // Data processing
        for (y = 0; y < 256; y++)
        {
            for (x = 0; x < 320; x++)
            {
                int temp = x * 256 + 255 - y;
                Spectrum1.X_Data_brut[bufferIndex][temp] = (UInt16)(*imgptr1++ + *imgptr1++ * 256);
                aquirData[temp] = Spectrum1.X_Data_brut[bufferIndex][temp];
            }
        }
        // (...)
    }
}

As shown, an AccessViolationException is triggered at the line where I cast the Wrapper pointer to a local byte pointer.

If I put a breakpoint on that line, I can see that the Wrapper pointer correctly points to a memory address, but says that it is unable to read memory, so the pointed data is never gathered in C#.

I have read that the C# equivalent of an unsigned char in C++ is a byte, so normally I should read the same amount of data and never go outside the boundaries of my data structure.

Additionnal information that could be useful:

Do you have any ideas how to fix this ?

Upvotes: 2

Views: 1766

Answers (1)

tukra
tukra

Reputation: 921

I'm not sure why you're getting an exception but I'd marshal it into a CLR array on the C++/CLI side so no unsafe code is needed on the C# side.

C++/CLI:

#include <vcclr.h>
#include <algorithm>

#pragma unmanaged

const int data_size = 100;

unsigned char * foo()
{
    auto p = new unsigned char[data_size];
    for (int i = 0; i < data_size; ++i)
        p[i] = (unsigned char)i;
    return p;
}

#pragma managed

public ref class Wrapper
{
public:
    array<unsigned char>^ data;

    void Start()
    {
        auto p = foo();
        data = gcnew array<unsigned char>(data_size);
        pin_ptr<unsigned char> data_pin = &data[0];
        std::copy(p, p+data_size, (unsigned char *)data_pin);
    }
};

C#:

class Program
{
    static void Main(string[] args)
    {
        var wrapper = new Wrapper();
        wrapper.Start();
        System.Console.WriteLine($"{wrapper.data[15]}");
    }
}

This will contain any possible problems close to the source and make debugging a lot less confusing. If it crashes in std::copy then you're just using your C++ object wrong.

Upvotes: 1

Related Questions