2016-08-12 6 views
1

Ich muss einige vorhandene .NET-Logik (d. H. Assembly MyManage.dll) zu systemeigenem Code verfügbar machen, also habe ich beschlossen, C++/CLI Brücke zu erstellen. Ich habe C++/CLI-Projekt erstellt und einen Verweis auf MyManage.dll hinzugefügt. Lange Rede kurzer Sinn - es funktioniert - mir ist es gelungen, auf alles zuzugreifen, was aus nativem Code zugänglich sein sollte.C++/CLI - C# Interop - Zeichenfolge Konvertierung Speicherverlust

Aber große Ausgabe ist, dass meine Implementierung Speicher verliert. Nach Tagen des Testens und Forschens habe ich das Problem auf System::String < ->const wchar_t Umwandlung eingegrenzt. Schließlich habe ich ein triviales C++/CLI-Projekt erstellt, die (reproduziert) die Ausgabe zeigt:

#define EXPORTED __declspec(dllexport) 

System::String^ ToManaged(const wchar_t* unmanagedString) 
{ 
    return gcnew System::String(unmanagedString); 
} 

const wchar_t* ToUnmanaged(System::String^ managedString) 
{ 
    return (wchar_t*) System::Runtime::InteropServices::Marshal::StringToHGlobalUni(managedString).ToPointer(); 
} 

EXPORTED const wchar_t* __stdcall GetString(const wchar_t* dummy) 
{ 
    return ToUnmanaged(ToManaged(dummy)); 
} 

(Wenn es nicht offensichtlich aus dem vorherigen Code ist - ich bin ziemlich neu in C++/CLI)

Wie ich bereits erwähnt habe, funktioniert der Code aber akkumuliert Speicherverbrauch, so dass es definitiv ein Leck in System::String < ->const wchar_t Umwandlung ist.

Meine Frage ist offensichtlich: wie zu implementieren String-Konvertierung ohne das Leck.

Danke!

+0

Ich würde COM und COM verwenden, um die Methoden zu aktivieren, auf die Sie zugreifen müssen. Verwenden Sie tlb.exe, um das COM-Interop zu generieren. Viel weniger Kopfschmerzen. Ich ging den Cli/C++ - Pfad und entdeckte Probleme wie Dll Load Order Probleme und Virus Software Probleme zu erstellen. – CKIsLearning

+0

@CKIsLearning - Danke für den Versuch zu helfen. Ehrlich, aus irgendeinem Grund bin ich ziemlich anti-COM orientiert. Ich würde dorthin gehen, wenn ich keine andere Lösung gefunden hätte, aber da ich inzwischen das Problem gelöst habe, gibt es keinen Grund dafür. Danke trotzdem! –

+0

Jeder Aufruf von Marshal :: StringToHGlobalUni() ** muss ** mit einem Aufruf von Marshal :: FreeCoTaskMem() gepaart werden. Das macht Ihre ToUnmanaged() Funktion ohne Hoffnung auf eine einfache Lösung gebrochen, dieser Aufruf wird nicht passieren. Sie müssen dies neu überdenken, überlegen Sie sich einen intelligenteren String-Typ wie std :: wstring –

Antwort

0

UPDATE: Bitte negativen Wähler ignorieren - wie Sie weigerte er sich selbst sehen zu erklären, was hier nicht stimmt. Verschiedene Leute haben unterschiedliche Motive ... Eines ist sicher: Lösung hier funktioniert perfekt, ohne Speicherleck.

Ich habe die Lösung gefunden (basierend auf Overview of Marshaling in C++ und marshal_context::marshal_as). So soll wie folgt geändert werden:

#include <msclr\marshal.h> 

System::String^ ToManaged(const wchar_t* unmanagedString) 
{ 
    return msclr::interop::marshal_as<System::String^>(unmanagedString); 
} 

gcroot<msclr::interop::marshal_context^> context; 

const wchar_t* ToUnmanaged(System::String^ managedString) 
{   
    msclr::interop::marshal_context^ unpacked = context; 

    if (unpacked != nullptr) 
     delete unpacked; 

    context = gcnew msclr::interop::marshal_context(); 

    return context->marshal_as<const wchar_t*>(managedString); 
} 

HINWEIS: Hier habe ich implementiert sehr ungeschickt Umgang mit marshal_context Beispiel -, wenn der nächste Anruf ankommt, wird das Ergebnis aus dem vorherigen Aufruf gelöscht. Diese Implementierung würde auseinander fallen in Szenario Multi-Threading, so dass Sie ein besserer mit dem folgend im Auge implementieren sollten:

  • marshal_context Instanz kann für mehrere Anrufe verwendet werden, aber es sollte ab und zu löschen (zu befreie den Speicher von zuvor gemarsarded Strings);
  • Sobald marshal_context gelöscht wird, werden auch alle const wchar_t* gelöscht, die mit dieser Datei erstellt wurden. Das bedeutet, dass Sie den Kontext nicht sofort nach der Verwendung löschen sollten. Sie müssen jedoch genügend Zeit bereitstellen, damit der Code die resultierende Zeichenfolge tatsächlich abrufen kann.
+0

Es wäre schön, wenn jemand, der das abstimmte, eine Erklärung liefern könnte. Ich kann bestätigen, dass die gelieferte Lösung wie Charme funktioniert, ohne irgendwelche Lecks. –

+0

Ich würde vermuten, dass Sie diesen Downvote aufgrund der spröden 'marshal_context' Behandlung verdient haben, also *" funktioniert wie Charme "* ist ziemlich relativ ... –

+0

@LucasTrzesniewski - Der Punkt der Antwort ist nicht in Copy-Paste-Verwendung der gleichen Genaues Codebeispiel, aber in dem Ansatz, der verwendet werden sollte - ''. Und dieser Ansatz ** funktioniert wie Charme ** (ich verwende es bereits in der Produktion mit 20.000 Anrufen pro Sekunde ohne Speicherverlust). Über das Codebeispiel - Ich habe bereits alles in der Antwort angegeben und einige Hinweise dazu gegeben, wie es * korrekt * implementiert werden kann. –

0

Sie müssen den Zeiger von StringToHGlobalUni freigeben, nachdem Sie es verwendet haben. Verwenden Sie Marshal.FreeHGlobal oder LocalFree.

+1

Danke für den Versuch zu helfen! Aber das hat nicht geholfen (ich habe es schon versucht). –

+0

Um genauer zu sein: In einem meiner vorherigen Versuche, das Problem zu lösen, habe ich eine Logik implementiert, die alle auf diese Weise erstellten 'IntPtr' nach einiger Zeit befreit, aber es hat nicht geholfen. –

+0

Oder um genauer zu sein: Es kann ein Problem sein, aber es ist nicht das einzige Problem sicher. –

0

Während nicht genau auf Ihre api gebaut, ich denke, das befasst sich mit der Erinnerung

HRESULT GetString(BSTR* p_bstrResult, unsigned long* ulErrCode) 
{ 
    HRESULT hr = S_OK; 

    try 
    { 
     System::String ^systemstring = gcnew System::String(""); 
        DotNetObject::o = gcnew DotNetObject:: DotNetObjectComponent(); 
     *ulErrCode = (unsigned long)o->GetString(systemstring); 
     pin_ptr<const wchar_t> wch = PtrToStringChars(systemstring); 
     _bstr_t bstrt(wch); 
     *p_bstrResult = bstrt.GetBSTR(); // native client babysits 
     delete systemstring;   
    } 
    catch(Exception^ex) 
    { 

    } 
    return hr; 

} 
+0

Danke für den Versuch zu helfen! Inzwischen habe ich die Lösung gefunden (wie Sie in meiner Antwort sehen können). Ich bin nicht sicher, ob Ihre Lösung auch funktioniert, aber es sieht komplizierter aus als die, die ich gefunden habe. Danke trotzdem! –