Search code examples
c#visual-studioc++-cliclr

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:

  • This project has been copied from another PC and the same code is functional on that PC.
  • Both PC have same Visual Studio, same .Net version, same SDK, both 64 bit compiled. Only Windows versions differ (working on Windows 8 and not working on Windows 7).
  • I unsuccessfully tried using Marshal functions.

Do you have any ideas how to fix this ?


Solution

  • 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.