GCHandle,Marshal,托管和非托管内存:固定或不固定

时间:2010-11-10 17:25:44

标签: c# .net c++-cli marshalling pinning

由于Hans Passant wishes这是我的情景。我有一个混合模式应用程序,其中本机代码执行所有艰苦的工作,同时尊重性能和托管代码只负责GUI。用户也将通过编写他们的专有C#代码参与。我有本地类的C ++,GUI和用户代码的C#以及介于两者之间的包装类的C ++ / Cli。在我的所有C ++类中,有一个执行%90的计算,每次都创建一个不同的参数。我们称之为NativeClass。有apprx。这个NativeClass的2000个实例,我必须在计算之前找到与某个参数相关的正确实例。所以我设计了一个hash_map,其参数是哈希码,用于此目的。当我得到一个参数时,我在hash_map中寻找正确的实例,我找到它并调用它的一些方法 当用户通过编写C#代码来控制计算时,该类通过回调执行这些代码。这是微不足道的,但有时我需要一些关于用户构建的.Net类的信息。所以我需要以某种方式将特定的ManagedClass附加到NativeClass。我的第一个解决方案是使用GChandle.Alloc()并传输句柄地址。但是有一些关于GC的concerns,它将无法正常工作。 Hans建议Marshal.AllocCoTaskMem()和Marshal.StructureToPtr()在非托管内存中分配托管对象,但我相信这对值类型类或结构有效。 ref类怎么样?如何在防止GC收集并使GC同时正常工作的同时传递对NativeClass的引用?

以下是一些示例代码:

class NativeClass
{
private:
    int AddressOfManagedHandle;
public:
    static NativeClass * GetNativeClassFromHashMap(int SomeParameter)
    {
// return NativeClass associated with SomeParameter from NativeClassHashMap;
    }
    NativeClass(int addr, int SomeParameter) : AddressOfManagedHandle(addr)
    {

    }
    int GetAddress(){return AddressOfManagedHandle;}
void DoCalculation(){ 
// CALCULATIONS
}
};


public ref class ManagedClass : MarshalByRefObject
{
private:
    NativeClass* _nc;
//GCHandle handle;
    void FreeManagedClass()
    {
        Marshal::FreeHGlobal(IntPtr(_nc->GetAddress()));
//if(handle.IsAllocated)
//handle.Free();
        delete _nc;
    }
public: 
    ManagedClass()
    {
        IntPtr addr = (Marshal::AllocHGlobal(Marshal::Sizeof(this))); // Error
        Marshal::StructureToPtr(this,addr,true);
//handle = GCHandle.Alloc(this);
//IntPtr addr = handle.ToIntPtr();
        _nc = new NativeClass(addr.ToInt32());
    }
    ~ManagedClass() {FreeManagedClass();}
    !ManagedClass() {FreeManagedClass();}
    int GetAddress() {return _nc->GetAddress();};
    static ManagedClass^ GetManagedClass(int SomeParameter)
    {
int addr = NativeClass::GetNativeClassFromHashMap(SomeParameter)->GetAddress();
//Object^obj = GCHandle::FromIntPtr(IntPtr(addr)).Target;
Object^ obj = Marshal::PtrToStructure(IntPtr(addr), ManagedClass::typeid );
    return dynamic_cast<ManagedClass^>(obj);

    }
};

我很抱歉它太长了而且还不清楚。

3 个答案:

答案 0 :(得分:3)

我花了很长时间与一个类似的问题作斗争,这是对我有用的解决方案的大纲....

  1. 将句柄存储为托管类void *
  2. 存储指向托管类中非托管类的指针(正如您所做的那样)
  3. 使用普通的newdelete而不是AllocHGlobal
  4. 等任何内容
  5. 转换void *和托管对象引用
  6. 之间的GCHandle
  7. 不要担心从MarshalByRefObject派生 - 您不需要它,因为您正在进行自己的编组
  8. 在释放托管类时记住防御性编码
  9. 我拿了你的上面的代码并入侵它以获得:

    class NativeClass
    {
    private:
        void * managedHandle;
    public:
        static NativeClass * GetNativeClassFromHashMap(int SomeParameter)
        {
            // return NativeClass associated with SomeParameter from NativeClassHashMap;
        }
        NativeClass(void *handle, int SomeParameter)
            : managedHandle(handle)
        {
        }
        void * ManagedHandle()
        {
            return managedHandle;
        }
        void DoCalculation()
        { 
            // CALCULATIONS
        }
    };
    
    public ref class ManagedClass
    {
    private:
        NativeClass* _nc;
        void FreeManagedClass()
        {
            if (_nc)
            {
                // Free the handle to the managed object
                static_cast<GCHandle>(IntPtr(_nc->ManagedHandle)).Free();
                // Delete the native object
                delete _nc;
                _nc = 0;
            }
        }
    public: 
        ManagedClass()
        {
            // Allocate GCHandle of type 'Normal' (see doco for Normal, Weak, Pinned)
            GCHandle gch = GCHandle::Alloc(this, GCHandleType::Normal);
            // Convert to void*
            void *handle = static_cast<IntPtr>(gch).ToPointer();
            // Initialise native object, storing handle to native object as void*
            _nc = new NativeClass(handle);
        }
        ~ManagedClass() {FreeManagedClass();}
        !ManagedClass() {FreeManagedClass();}
        static ManagedClass^ GetManagedClass(int SomeParameter)
        {
            // Native class is retrieved from hash map
            NativeClass *nc = NativeClass::GetNativeClassFromHashMap(SomeParameter);
            // Extract GCHandle from handle stored in native class
            // This is the reverse of the process used in the ManagedClass constructor
            GCHandle gch = static_cast<GCHandle>(IntPtr(nc->ManagedHandle()));
            // Cast the target of the GCHandle to the managed object
            return dynamic_cast<ManagedClass^>(gch.Target);
        }
    };
    

    这应该会让你走上正轨。

答案 1 :(得分:0)

答案 2 :(得分:0)

嗯。

GCHandle是一个结构。

在某些情况下,通过未固定的GCHandle参考将会做你想要的。