【问题标题】:C# pointer on unmanaged data from C++ CLI project来自 C++ CLI 项目的非托管数据的 C# 指针
【发布时间】:2018-01-17 19:50:52
【问题描述】:

我有一个用 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];
            }
        }
        // (...)
    }
}

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

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

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

可能有用的其他信息:

  • 此项目已从另一台 PC 复制,相同的代码在该 PC 上运行。
  • 两台 PC 具有相同的 Visual Studio、相同的 .Net 版本、相同的 SDK,均编译为 64 位。仅 Windows 版本不同(适用于 Windows 8,不适用于 Windows 7)。
  • 我尝试使用 Marshal 函数没有成功。

您有任何解决方法的想法吗?

【问题讨论】:

  • 所以人们来这里只是为了投反对票还是他们真的有什么想法?
  • 是的,我不明白有人喜欢为 C++/CLI Q 提供的毯子 -1。你能告诉我们'ptr'的声明吗?我想知道您的 Wrapper 中的成员是否需要是 pin_ptr
  • 我已经编辑了我的问题

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


【解决方案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++ 对象。

【讨论】:

  • 所以如果我理解正确的话,我只是直接在 C++ 中分配内存,在 C++ 中创建一个可以从 C# 直接访问的数据结构?我想知道为什么我以前没有想到这一点!但是,#pragma managed/unmanaged 的​​目的是什么?
  • #pragma managed/unmanaged 让您在单个编译单元中同时拥有托管和非托管代码。我只是在这里用它来清楚地表明 foo() 是非托管代码。
  • 是的,对于数据,我立即分配托管数组并将数据从本机 C++ 堆复制到其中。然后 C# 代码只处理托管对象,如果出现问题,它将立即失败。
  • 是的,它有效,抱歉我还没有接受答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-12
  • 1970-01-01
  • 1970-01-01
  • 2021-07-26
相关资源
最近更新 更多