1

我有一个非托管类,它接受指向内存的指针作为其存储空间。

例如

class MemBlock
{
    void* mpMemoryBlock;

    // Various other variables that manipulate the memory block goes here.
public:
    MemBlock( void* pMemoryBlock ) :
        mpMemoryBlock( pMemoryBlock )
    {
    }

    // A load of code that does operation on the memory block goes here.
};

现在我正在尝试包装这个类以便在 C# 中使用。显然,我希望能够将类似 a 的东西传递float[]给班级。显而易见的事情是cli::pin_ptr从包装类中使用。

public ref class MemBlockWrapper
{
    MemBlock* mpMemBlock;
public:
    MemBlockWrapper( array< float >^ fltArray )
    {
        cli::pin_ptr< float > pFltArray = &fltArray[0];

        // Brilliant we can now pass this pinned array to the C++ code.
        mpMemBlock  = new MemBlock( (void*)pFltArray );

        // Now the pin_ptr goes out of scope ...
    }
}

但是,固定 ptr 仅在 cli::pin_ptr 在范围内时才有效。构造函数退出的那一刻,我不能再保证 C++ 类拥有的内存块是真实的。

有没有办法在类的生命周期内固定一个指针?我做了很多搜索,只找到了一种使用“GCHandle”的方法,该方法似乎纯粹用于托管 C++(即不是 C++/CLI)。有人可以指出我确定性地固定和取消固定指针的方法吗?

4

2 回答 2

2

警告:直接回答了这个问题,但在你尝试这个之前,请先阅读Hans 的回答并确保你真的理解发生了什么并且仍然想这样做。

固定GCHandle就可以完成这项工作,它可以从 C++/CLI 中使用。显然,只要确保手柄是Pinned类型。

这是一个例子:

public ref class MemBlockWrapper
{
    MemBlock* mpMemBlock;
    System::Runtime::InteropServices::GCHandle hFltArray;
public:
    MemBlockWrapper(array< float >^ fltArray)
    {
        hFltArray = System::Runtime::InteropServices::GCHandle::Alloc(
            fltArray,
            System::Runtime::InteropServices::GCHandleType::Pinned);

        mpMemBlock = new MemBlock(hFltArray.AddrOfPinnedObject().ToPointer());
    }

    ~MemBlockWrapper()
    {
        this->!MemBlockWrapper();
    }

    !MemBlockWrapper()
    {
        if (mpMemBlock)
        {
            delete mpMemBlock;
            mpMemBlock = nullptr;
            hFltArray.Free();
        }
    }
};

我已经添加了一个析构函数和一个终结器,这样Disposable即使你忘记处理你的包装器,你也可以获得模式和安全清理。

于 2015-12-14T22:33:30.810 回答
2

当然,你不能使用 pin_ptr<>。GCHandle 在 VB.NET 中与在 C# 中一样可用,在 C++/CLI 中与在“托管 C++”中一样可用。顺便说一句,后两者之间没有区别,只是语法不同。GCHandle 是一种普通的 .NET 类型,可用于任何 .NET 兼容语言。

不,您正在考虑的是一个非常糟糕的主意。长时间固定数组是非常邪恶的。其中“长”取决于垃圾收集器的运行速度,通常固定超过几秒钟是非常糟糕的,几分钟就超出了苍白。

固定对象是垃圾收集器在压缩堆时必须不断移动的道路上的一块石头,它会降低堆的使用效率。当它是堆段中唯一剩余的对象时开始出现真正的问题,它不能被回收并且该数组现在花费你两兆字节。在服务器上添加几只手和脚。

一旦你得到了少数,你接下来考虑只是简单地复制数组。从您使用 new[] 运算符分配的array<float>^a 。float[]没什么大不了的,以每秒 7 GB 的速度运行,给予或接受。

于 2015-12-14T22:42:44.507 回答