GCHandle, маршал, управляемая и неуправляемая память: закрепить или не закрепить - PullRequest
5 голосов
/ 10 ноября 2010

Как Ганс Пассант желает вот мой сценарий.У меня есть приложение в смешанном режиме, в котором нативный код выполняет всю тяжелую работу, сохраняя при этом производительность, а управляемый код отвечает только за графический интерфейс.Также пользователи будут участвовать, написав свой собственный код C #.У меня есть C ++ для нативных классов, C # для GUI и пользовательского кода и C ++ / Cli для классов-оболочек между ними.Среди всех моих классов C ++ есть один, который выполняет 90% вычислений и каждый раз создает новый параметр.Давайте назовем это NativeClass.Есть ок.2000 экземпляров этого NativeClass, и я должен найти правильный экземпляр, связанный с каким-либо параметром, прежде чем он выполнит вычисление.Поэтому для этой цели я разработал hash_map с параметрами, являющимися хеш-кодом.Когда я получаю параметр, я ищу нужный экземпляр в hash_map, нахожу его и вызываю некоторые из его методов.
Когда пользователи выполняют вычисления, написав код C #, и этот класс выполняет эти коды с помощью обратных вызовов.Это тривиально, но иногда мне нужна информация о классах .Net, созданных пользователями.Поэтому мне нужно каким-то образом прикрепить этот конкретный ManagedClass к NativeClass.Моим первым решением является использование GChandle.Alloc () и передача адреса дескрипторов.Но есть некоторые опасения относительно GC, что он не будет выполнять свою работу должным образом.Ганс рекомендовал 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);

    }
};

Извините, что онслишком долго и все еще не ясно.

Ответы [ 3 ]

3 голосов
/ 12 ноября 2010

Я потратил довольно много времени на борьбу с подобной проблемой, и вот схема решения, которое работало для меня ....

  1. Сохранить дескриптор для управляемого класса как void *
  2. Сохранить указатель на неуправляемый класс в управляемом классе (как вы сделали)
  3. Используйте простые старые new и delete, а не что-либо, например 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);
    }
};

Это должно поставить вас на правильный путь.

0 голосов
/ 12 ноября 2010

Хммм.

GCHandle - это структура.

В некоторых случаях передача неподкрепленного рефери GCHandle будет делать то, что вы хотите.

0 голосов
/ 11 ноября 2010
...