来自C ++ CLI项目的非托管数据的C#指针

时间:2018-01-17 19:50:52

标签: c# visual-studio c++-cli clr

我有一个用C ++编写的SDK来管理设备。我控制设备的程序是用C#编写的,所以CLI包装器类自然会在两种语言之间进行转换。我的C#项目将包装器包含为DLL。

我的问题是C ++ SDK使用指针指向数据数组。这些指针也可以在包装器中使用。

Wrapper .cpp代码:

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

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

此代码初始化设备并创建指向数据结构的指针(unsigned char数组)。

Wrapper SDK .cpp代码:

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

Wrapper .h代码:

public ref class Wrapper
{
    public:
        Wrapper();

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

    private:
        // (...)
};

代码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];
            }
        }
        // (...)
    }
}

如图所示,在我将Wrapper指针强制转换为本地字节指针的行上触发了AccessViolationException。

如果我在该行上设置了一个断点,我可以看到Wrapper指针正确指向一个内存地址,但是说它无法读取内存,因此指向的数据永远不会被收集在C#中。

我已经读过C ++中C#等效的无符号字符是一个字节,所以通常我应该读取相同数量的数据,并且永远不会超出我的数据结构的边界。

可能有用的附加信息:

  • 此项目已从另一台PC复制,相同的代码在该PC上正常运行。
  • 两台PC都有相同的Visual Studio,相同的.Net版本,相同的SDK,都是64位编译。只有Windows版本不同(适用于Windows 8而不适用于Windows 7)。
  • 我尝试使用Marshal函数失败了。

你有任何想法如何解决这个问题?

1 个答案:

答案 0 :(得分:1)

我不确定为什么你会得到一个异常,但我会把它编组到C ++ / CLI端的CLR数组中,所以在C#端不需要不安全的代码。

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]}");
    }
}

这将包含靠近源的任何可能的问题,并使调试更容易混淆。如果它在std :: copy中崩溃,那么你只是错误地使用你的C ++对象。