2010-11-10 8 views
5

Als Hans Passantwishes hier ist das Szenario von mir. Ich habe eine Anwendung im gemischten Modus, in der der native Code die ganze Arbeit unter Einhaltung der Performance erledigt und verwalteter Code nur für die GUI zuständig ist. Auch Benutzer werden teilnehmen, indem sie ihren proprietären C# -Code schreiben. Ich habe C++ für native Klassen, C# für GUI und Benutzercode und C++/Cli für Wrapper-Klassen dazwischen. Unter all meinen C++ - Klassen gibt es eine, die% 90 der Berechnungen durchführt und jedesmal einen anderen Parameter erzeugt. Nennen wir es NativeClass. Es gibt apprx. 2000 Instanzen dieser NativeClass und ich müssen die richtige Instanz in Bezug auf einige Parameter finden, bevor sie berechnet. Also habe ich eine Hash-Map entwickelt, deren Parameter der Hash-Code ist. Wenn ich einen Parameter erhalte, suche ich nach der richtigen Instanz in hash_map, finde sie und rufe einige ihrer Methoden auf.
Wenn Benutzer zu Berechnungen durch Schreiben von C# -Code und dieser Klasse beitragen, führen Sie diese Codes durch Rückrufe aus. Das ist trivial, aber manchmal brauche ich einige Informationen über die .NET-Klassen, die Benutzer erstellt haben. Also muss ich diese bestimmte ManagedClass irgendwie an NativeClass anhängen. Meine erste Lösung ist GChandle.Alloc() und Übertragung der Handles-Adresse. Aber es gibt einige concerns über GC, dass es seine Arbeit nicht richtig machen wird. Hans empfahl die Marshal.AllocCoTaskMem() und Marshal.StructureToPtr(), um verwaltete Objekte in nicht verwaltetem Speicher zuzuordnen, jedoch glaube ich, dass dies für Werttypklassen oder -strukturen gültig ist. Wie wäre es mit Ref-Klassen? Wie kann ich einen Verweis auf NativeClass übergeben, während verhindert wird, dass GC erfasst wird und GC gleichzeitig ordnungsgemäß funktioniert? HierGCHandle, Marshal, verwalteten und nicht verwalteten Speicher: Zu pinnen oder nicht zu pinnen

ist ein Beispielcode:

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

    } 
}; 

Es tut mir leid, dass es toooooo lang ist und noch nicht klar.

+1

Sie sollten IntPtr anstelle von int verwenden, um native Zeiger zu speichern. Andernfalls kann Ihr Code auf 64-Bit-Windows abstürzen. – Elmue

+0

Sie sollten den Speicher in einer anderen Klasse nicht freigeben. Schreiben Sie einen Destruktor (Finalizer) ~ NativeClass(), der FreeHglobal() aufruft. – Elmue

Antwort

3

Ich verbrachte eine ganze Weile mit einem ähnlichen Problem zu kämpfen, und dies ist der Umriss der Lösung, die für mich ....

arbeitete
  1. Lagern Sie den Griff in die verwaltete Klasse als void *
  2. Shop ein Zeiger auf die nicht verwaltete Klasse in der verwalteten Klasse
  3. Verwenden plain old new und delete anstatt etwas wie AllocHGlobal
  4. die GCHandle Transformation zwischen dem voi (wie Sie getan haben) d * und verwaltet Objektreferenz
  5. Kümmere dich nicht um von MarshalByRefObject abzuleiten - Sie brauchen es nicht wie Sie Ihre eigenen Rangier tun
  6. defensive Codierung Denken Sie daran, wenn die verwaltete Klasse befreien

Ich habe Ihren obigen Code und gehackt um es zu erhalten:

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

Dies sollte Sie auf den richtigen Weg bringen.

+2

Der Vorteil dieser Methode gegenüber der Verwendung von gcroot ist, dass NativeClass in einer separaten C++ - DLL sein kann, die von einem C++/CLI-Wrapper konsumiert wird. Das war was ich brauchte. – mcdave

+0

Ja mein eigener Code, den ich gerade benutze, ist genau wie deiner. Dinge mit Marshalklasse funktionieren nicht. Aber ich stellte die Frage, um sicher zu sein, was vor sich geht. Einzige Unterschiede sind, dass Sie kein Handle für ManagedClass in der ManagedClass speichern und NativeClass die Handle-Adresse als Zeiger nicht als Integer hat. Lass mich 3 Fragen stellen. Wie lange läuft dieser Code? Haben Sie Probleme mit dem Speicher? Worum geht es bei Ihrer Bewerbung? –

+0

Arbeite seit ~ 3 Jahren; Keine Speicherprobleme, es sei denn, Sie vergessen, das verwaltete Handle freizugeben. In diesem Fall werden die verwalteten Objekte niemals als Garbage Collection behandelt. Die Anwendung ist eine technische, die, wie Ihre, die Leistung von nativem Code erfordert, während die Benutzeroberfläche in .NET vorhanden ist. – mcdave

0

Hmmm.

GCHandle ist eine Struktur.

In einigen Fällen wird die Weitergabe eines unverankerten GCHandle ref das tun, was Sie wollen.

+0

Nun, ich dachte schon. GCHandle.Alloc mit GCHandleTypes.Normal scheint mir ok. Das sagt MSDN: 'Dieser Handle-Typ stellt ein undurchsichtiges Handle dar, was bedeutet, dass Sie die Adresse des gepinnten Objekts nicht über das Handle auflösen können. Sie können diesen Typ verwenden, um ein Objekt zu verfolgen und seine Erfassung durch den Garbage Collector zu verhindern. Dieses Enumerationselement ist nützlich, wenn ein nicht verwalteter Client die einzige Referenz enthält, die vom Garbage Collector nicht erkannt werden kann, in ein verwaltetes Objekt. "On GCHandle.Normal. –