2010-11-10 24 views
5

As Hans Passantwishes這裏是我的場景。我有一個混合模式的應用程序,其中本機代碼在尊重性能的同時完成所有艱苦的工作,託管代碼僅負責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類?我如何將引用傳遞給NativeClass,同時阻止它們被GC收集並使GC在同一時間正常工作?GCHandle,Marshal,managed and unmanaged memory:To pin or Not To Pin

下面是一些示例代碼:

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

    } 
}; 

對不起,這是toooooo長,目前尚不清楚。

+1

您應該使用IntPtr而不是int來存儲本機指針。否則,您的代碼可能會在64位Windows上崩潰。 – Elmue 2014-09-01 15:28:31

+0

您不應該釋放其他課程中的內存。編寫一個調用FreeHglobal()的析構函數(finalizer)〜NativeClass()。 – Elmue 2014-09-01 15:33:35

回答

3

我花了相當長的一段類似的問題對抗,這是對我工作的解決方案的輪廓....

  1. 商店的句柄管理類爲void *
  2. 商店一個指向託管類的非託管類(如您已完成)
  3. 使用普通的舊newdelete,而不是任何東西,如AllocHGlobal
  4. 改造GCHandle的VOI之間d *和管理對象的引用
  5. 不要擔心從MarshalByRefObject推導 - 釋放託管類

我把當你不需要它,你在做你自己的編組

  • 記住防禦性編碼你上面的代碼,並在其砍死得到:

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

    這應該把你在正確的軌道上。

  • +2

    此方法優於使用gcroot 的優點是NativeClass可以位於由C++/CLI包裝器使用的單獨的C++ DLL中。這是我需要的。 – mcdave 2010-11-12 12:35:52

    +0

    是我自己的代碼,我現在正在使用,就像你的代碼一樣。元帥班級不起作用。但我問這個問題只是爲了確定發生了什麼。唯一的區別是您不會將ManagedClass的句柄存儲在ManagedClass中,並且NativeClass將句柄地址作爲指針而不是整數。讓我問3個問題。這段代碼運行了多久?你有沒有與記憶相關的問題?你的申請是關於什麼的? – 2010-11-12 13:44:31

    +0

    已工作約3年;除非忘記釋放託管句柄,否則不會出現內存問題,在這種情況下,託管對象不會被垃圾收集;該應用程序是一種技術應用程序,與您的.NET一樣,它需要在使用UI的同時執行本機代碼。 – mcdave 2010-11-13 22:25:46

    0

    嗯。

    GCHandle是一個結構體。

    在某些情況下,傳遞未固定的GCHandle ref會執行您想要的操作。

    +0

    嗯,我是這麼認爲的。 GCHandle.Alloc和GCHandleTypes.Normal對我來說似乎沒問題。這就是MSDN所說的「這個句柄類型表示一個不透明句柄,這意味着你不能通過句柄來解析固定對象的地址。您可以使用此類型來跟蹤對象並阻止垃圾收集器收集它。這個枚舉成員在非託管客戶端持有從垃圾收集器不可檢測到的唯一引用到GCHandle.Normal上的託管對象時非常有用。 – 2010-11-12 07:51:20