5

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

这是一些示例代码:

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

    }
};

很抱歉,它太长了,仍然不清楚。

4

3 回答 3

3

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

  1. 将托管类的句柄存储为void *
  2. 在托管类中存储指向非托管类的指针(如您所做的那样)
  3. 使用普通的旧newdelete不是任何东西,例如AllocHGlobal
  4. GCHandle在 void * 和托管对象引用之间进行转换
  5. 不要担心派生MarshalByRefObject- 你不需要它,因为你正在做你自己的编组
  6. 释放托管类时记住防御性编码

我拿走了你上面的代码并破解了它以获得:

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

这应该使您走上正确的轨道。

于 2010-11-12T12:25:23.173 回答
0

gcroot<ManagedClass^>让这变得容易多了

于 2010-11-11T19:06:11.327 回答
0

嗯。

GCHandle 是一个结构。

在某些情况下,传递未固定的 GCHandle ref 会做你想做的事。

于 2010-11-11T23:28:23.667 回答