代码之家  ›  专栏  ›  技术社区  ›  Nicola

C++CLI项目中非托管数据上的C#指针

  •  1
  • Nicola  · 技术社区  · 7 年前

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

    我的问题是,C++SDK使用指针指向数据数组。包装器中也提供了这些指针。

    包装器。cpp代码:

    Wrapper::Wrapper()
    {
        myData = new DataAquis(); //initiate C++ class's instance
    }
    
    int Wrapper::Start()
    {
        //(..)
        int num = myData->Start();
        ptr = (myData->img);
        return num;
    }
    

    此代码初始化设备并创建指向数据结构(无符号字符数组)的指针。

    包装器SDK。cpp代码:

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

    包装器。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];
                }
            }
            // (...)
        }
    }
    

    如图所示,在将包装器指针转换为本地字节指针的那一行触发AccessViolationException。

    如果我在那一行上放置一个断点,我可以看到包装器指针正确地指向了一个内存地址,但表示它是 无法读取内存 ,因此不会在C#中收集指向的数据。

    我已经读到,C++中无符号字符的C#等价物是一个字节,因此通常我应该读取相同数量的数据,并且永远不要超出数据结构的边界。

    可能有用的其他信息:

    • 此项目已从另一台电脑上复制,该电脑上的代码与此相同。
    • 两台PC都有相同的Visual Studio,相同。Net版本,相同的SDK,都是64位编译的。只有Windows版本不同(在Windows 8上工作而不在Windows 7上工作)。
    • 我尝试使用封送处理函数失败。

    你有什么办法来解决这个问题吗?

    1 回复  |  直到 7 年前
        1
  •  1
  •   tukra    7 年前

    我不知道为什么会出现异常,但我会在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++对象。