有效地调用非托管方法将非托管对象作为托管代码中的参数

时间:2011-04-14 00:41:52

标签: visual-c++ c++-cli

我有以下情况。托管代码将初始化一个类的大量对象,该类是非托管结构的包装器。我可以为此做两种方法。一种是拥有一个托管类包装器,它只有一个指向非托管对象的指针。另一种方法是拥有一个完整的托管类,并在需要调用非托管方法时创建非托管对象。我提供了以下两种方法。有人告诉我,如果我使用方法1(指向无人对象的指针),GC会有很多问题知道非托管部分,最好做方法2.有人告诉我哪个更好或者是否有是一些更好的方法。我对方法2的关注是,每次调用非托管方法时都会来回复制。我不确定GC问题是否超过它。

编辑 - 第一种方法有一个ref类,第二种方法有一个值类。第二个是值的原因是它可以更有效地添加到列表中

非托管:

struct A_UNMANAGED
{
    int a;
    int b[20];
};

void GetData(A_UNMANAGED& a); // populates A

在托管(第一种方法)

public ref class A_MANAGED
{
    A_UNMANGED* ap;

public:
    property System::UInt32 a
    {
        System::UInt32 get() { return ap->a; }
        void set(System::UInt32 value) { ap->a = value; }
    }

    property array<System::UInt32>^ b
    {
        array<System::UInt32>^ get() { return ap->b; }
        void set(array<System::UInt32>^ value) { b = value; } // assume this copy works
    }

internal:
    void GetData()
    {
        GetData(ap);
    }
};

在托管(第二种方法)(编辑:更新到参考。假设所有垃圾收集和指针创建都正确写入)

public value class A_MANAGED
{
    System::UInt32 a;
    array<System::UInt32>^ b;

public:
    property System::UInt32 a
    {
        System::UInt32 get() { return a; }
        void set(System::UInt32 value) { a = value; }
    }

    property array<System::UInt32>^ b
    {
        array<System::UInt32>^ get() { return b; }
        void set(array<System::UInt32>^ value) { b = value; }
    }

internal:
    void GetUnmanaged(A_UNMANAGED& obj1)
    {
        obj1.a = a; 
        pin_ptr<System::UInt32> bp = &b[0];
        memcpy(obj1.b, bp, 20);
    }

    void GetData()
    {
        A_UNMANAGED obj2;
        GetUnmanaged(obj2);
        GetData(obj2);
        // copy from obj2 to member variables
    }
};

2 个答案:

答案 0 :(得分:1)

不,第一个片段是规范的方式。垃圾收集器只移动指针,它不移动指向的对象。那个应该用malloc()或new运算符分配,它不能被移动。

您的代码中还有几个严重问题。您似乎没有为A_UNMANAGED分配内存,除非GetData()通过引用获取其参数。永远不会调用GetData()。这通常必须是ref类(不是ref值),因此您可以提供析构函数和终结器来释放内存。 b属性setter将使用StackOverflowException轰炸您的程序。在处理这个项目之前一定要学习这门语言。

检查this answer以获取示例代码。

答案 1 :(得分:1)

汉斯说,第一种方式是通常的方法(虽然我个人认为P / Invoke在这个特定的案例中会更简洁......)。但是,您的A_MANAGED::b实现将无法正常工作,如果只是尝试编译它,这将是显而易见的。试试这个:

public ref class A_MANAGED
{
    A_UNMANAGED* ap;

public:
    A_MANAGED() : ap(new A_UNMANAGED() ) { }
    ~A_MANAGED() { this->!A_MANAGED(); }
    !A_MANAGED() { delete ap; ap = nullptr; }

    property int a
    {
        int get() { return ap->a; }
        void set(int value) { ap->a = value; }
    }

    property array<int>^ b
    {
        array<int>^ get()
        {
            using System::Runtime::InteropServices::Marshal;
            array<int>^ arr = gcnew array<int>(20);
            Marshal::Copy(System::IntPtr(ap->b), arr, 0, 20);
            return arr;
        }
        void set(array<int>^ value)
        {
            using System::Runtime::InteropServices::Marshal;
            Marshal::Copy(value, 0, System::IntPtr(ap->b), 20);
        }
    }

internal:
    void GetData()
    {
        ::GetData(*ap);
    }
};

然后有关于从属性返回数组的常见警告:这是一个糟糕的想法。除非确实想要与非托管类的公共接口保持奇偶校验,否则b应该是一对set / get函数而不是属性。