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

问题描述:

我有以下情况。托管代码将初始化很多类的对象,这是一个非托管结构的包装器。有两种方法我可以做到这一点。一个是有一个托管类包装器只有一个指向非托管对象的指针。另一种是拥有一个完整的托管类,并在需要调用非托管方法时创建非托管对象。我已经提供了以下两种方法。我被告知,如果我使用方法1(有一个指针到未绑定的对象),GC将有很多问题知道非托管部分,最好是做方法2.有人告诉我哪个更好或如果有是一些其他的方法,甚至更好。我对方法2的关注是每次调用非托管方法时都进行复制。我不知道GC的问题是否超过它。

I have the following scenario. The managed code will initialize lots of object of a class which is a wrapper around an unmanaged struct. There are two approaches that I can do for this. One is to have a managed class wrapper that just has a pointer to the unmanaged object. The other is to have a full fledged managed class and create the unmanaged object when required to call into unmanaged methods. I have provided both the methods below. I was told that if I use the approach 1(having a pointer to unmanged object), the GC will have lots of issue knowing about the unmanaged portion and it is better to do approach 2. Does someone tell me which is better or if there is some other approach that is even better. My concern with Approach 2 is that there are copying to and fro everytime a unmanaged method is called. I am not sure if the GC issue outweighs it.

EDIT-第一种方法有一个ref类,第二种有一个值类。

EDIT- the first approach has a ref class and the second has a value class. The reason the second is value is so that it can be added to lists more efficiently

在非管理中:

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

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







In managed (First Approach)

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






在托管(第二种方法)


In managed (Second Approach) ( updated to ref. Assume all the garbage collection and pointer creation is written correctly)

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
    }
};


Hans说,第一种方法是通常的方法(虽然个人,我认为P / Invoke将在这个特定案例中更简洁)。但是,您的 A_MANAGED :: b 实现将不起作用,如果尝试简单地编译它,这将是显而易见的。尝试这样:

As Hans said, the first way is the usual approach (though personally, I think P/Invoke would be more succinct in this particular case...). However, your A_MANAGED::b implementation will not work, which would be obvious if one were to try simply compiling it. Try this instead:

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函数,而不是一个属性。

And then there's the usual caveat about returning arrays from properties: it's a bad idea. Unless you really want to maintain parity with the unmanaged class' public interface, b should really be a pair of set/get functions rather than a property.