Wie einen String mit unbekannter größe in ein char konvertieren?



  • _matze schrieb:

    std::string hat eine Methode c_str, CString (MFC) hat GetBuffer, also wird String^ auch irgendwas in der Richtung haben (irgendeine Methode also, die dir einen const char* oder so zurückgibt).

    EDIT: ToCharArray vielleicht?

    Ich bekomme ToCharArray nicht angewendet!

    also char* anfrage = anfrageString->ToCharArray funktioniert nicht!

    nur array<Char> ^anfrage = anfrageString->ToCharArray;

    aber wie der name schon verät Char nicht char!
    das scheint was anderes zu sein!



  • System::Char entspricht wchar_t , nicht char . Einen Array von Chars könntest du über volatile pin_ptr<wchar_t> ansprechen. Ansonsten bleibt nur noch der Weg über Marshaling 😉

    #include <iostream>
    
    using namespace std;
    using namespace System;
    using namespace System::Runtime::InteropServices;
    
    int main()
    {
        String ^foo = L"abcdefghijk";
        IntPtr cptr = Marshal::StringToHGlobalAnsi(foo);
        const char *pfoo = static_cast<const char *>(cptr.ToPointer());
        cout << pfoo << endl;
        Marshal::FreeHGlobal(cptr); // Nicht vergessen zu freen...
        return 0;
    }
    

    MfG



  • /rant/ schrieb:

    System::Char entspricht wchar_t , nicht char . Einen Array von Chars könntest du über volatile pin_ptr<wchar_t> ansprechen. Ansonsten bleibt nur noch der Weg über Marshaling 😉

    #include <iostream>
    
    using namespace std;
    using namespace System;
    using namespace System::Runtime::InteropServices;
    
    int main()
    {
        String ^foo = L"abcdefghijk";
        IntPtr cptr = Marshal::StringToHGlobalAnsi(foo);
        const char *pfoo = static_cast<const char *>(cptr.ToPointer());
        cout << pfoo << endl;
        Marshal::FreeHGlobal(cptr); // Nicht vergessen zu freen...
        return 0;
    }
    

    MfG

    const char* String_to_char(String^ _String) {
        IntPtr cptr = Marshal::StringToHGlobalAnsi(_String);
        const char *_char = static_cast<const char *>(cptr.ToPointer());
        //cout << pfoo << endl;
        Marshal::FreeHGlobal(cptr);
    	return _char;
    }
    
    1>El Em Ci.obj : error LNK2020: Nicht aufgelöstes Token (06000008) AutoMySQL::String_to_char.
    1>AutoMySQL.obj : error LNK2020: Nicht aufgelöstes Token (06000003) AutoMySQL::String_to_char.
    


  • const char* String_to_char(String^ _String) {
        IntPtr cptr = Marshal::StringToHGlobalAnsi(_String);
        const char *_char = static_cast<const char *>(cptr.ToPointer());
        cout << _char << endl;
        Marshal::FreeHGlobal(cptr);
    	return _char;
    }
    
    1>.\AutoMySQL.cpp(55) : error C2065: 'cout': nichtdeklarierter Bezeichner
    1>.\AutoMySQL.cpp(55) : error C2065: 'endl': nichtdeklarierter Bezeichner
    


  • Also sorry ist keine CLR!
    Sondern eine Win32!

    ich denke mal daher das std::cout sowieso raus fliegt
    aber #include <iostream> habe ich mal trotzdem gemacht

    habe aber eine Fehlermeldung

    1>El Em Ci.obj : error LNK2020: Nicht aufgelöstes Token (06000008) AutoMySQL::String_to_char.
    1>AutoMySQL.obj : error LNK2020: Nicht aufgelöstes Token (06000003) AutoMySQL::String_to_char.
    


  • LiGERWooD schrieb:

    const char* String_to_char(String^ _String) {
        IntPtr cptr = Marshal::StringToHGlobalAnsi(_String);
        const char *_char = static_cast<const char *>(cptr.ToPointer());
        cout << _char << endl;
        Marshal::FreeHGlobal(cptr);
    	return _char;
    }
    

    Das darfst du sowieso nicht! Einen Zeiger auf nicht mehr allokierten Speicher zurückgeben ist nicht gut 😮

    Was denn nun, sind wir im falschen Forum oder was?^^

    MfG



  • Also genau genommen ist mein Projekt bestanteil aus .Net Visual C++ aber mit einer .Net Framework umgebung, also Windows Forms und so, aber nach wie vohr ist es schon so das es eine

    int main() {
        return 0;
    }
    

    gibt.
    ich wollte jetzt natürlich deinen programmablauf in eine Funktion packen damit ich es einfach immer wieder verwenden kann, etwa so:

    String^ Ergebnis = "12,5";
    char* wert = String_to_char(Ergebnis);
    

    Was ja eigentlich weiter kein Promlem sein dürfte da man ja jede anweisung unter einer Funktion in C/C++ schreibt, nur das dann diese Funktion ja extra für deinen demonstrierten Programmablauf ist und halt natürlich das ergebnis zurück geben sol, also eine const char*

    Oder weis ich das nicht und man kann gar keine Funtion mit dem datentyp char defenieren? oder wie kann man es machen so das die char mit dem zugewiessenen ergebnis

    const char *pfoo = static_cast<const char *>(cptr.ToPointer());
    

    zurück gegeben wird?



  • hat es vieleicht damit was zu tun das ich die Funktion in einer clase definiere und nicht in der main.cpp?



  • Du solltest dich unbedingt entscheiden was du willst. .Net oder nicht .Net. Sonst bekommt man genau das Problem welches du hast, dass man zwischen managed Code und unmanaged Code Typen marshallen muss. Das ist oftmals einfach unnötig, bisher hast du auch noch keinen Grund genannt warum du C++ und .Net Datentypen mischen müsstest.



  • Talla schrieb:

    Du solltest dich unbedingt entscheiden was du willst. .Net oder nicht .Net. Sonst bekommt man genau das Problem welches du hast, dass man zwischen managed Code und unmanaged Code Typen marshallen muss. Das ist oftmals einfach unnötig, bisher hast du auch noch keinen Grund genannt warum du C++ und .Net Datentypen mischen müsstest.

    ähm diese Marschallmethode hat mir /rant/ gennant, dann wusste er bis dato nicht das ich .Net verwende, ansonsten decke ich mal das du mein eigentliches Problem verstanden hast das ich einen String in char bekommen muss, weil die funktion die es so braucht stammt von einer libary ab, also dahinter steckt assembler, und diese funktion ist in C aber ich brauche diese funktion für einen vorgang im hintergund zu einer .Net Framework anwendung.



  • LiGERWooD schrieb:

    Talla schrieb:

    Du solltest dich unbedingt entscheiden was du willst. .Net oder nicht .Net. Sonst bekommt man genau das Problem welches du hast, dass man zwischen managed Code und unmanaged Code Typen marshallen muss. Das ist oftmals einfach unnötig, bisher hast du auch noch keinen Grund genannt warum du C++ und .Net Datentypen mischen müsstest.

    ähm diese Marschallmethode hat mir /rant/ gennant, dann wusste er bis dato nicht das ich .Net verwende, ansonsten decke ich mal das du mein eigentliches Problem verstanden hast das ich einen String in char bekommen muss, weil die funktion die es so braucht stammt von einer libary ab, also dahinter steckt assembler, und diese funktion ist in C aber ich brauche diese funktion für einen vorgang im hintergund zu einer .Net Framework anwendung.

    Nun, nicht ganz. Ich habe dir diese Marshaler gebracht, weil ich davon ausgegangen bin, dass du beides (also .NET und nativen Code) verwendest. Ansonsten würden Marshaler in dieser Form ja keinen Sinn ergeben. Du kannst diesen Marshaler benutzen, aber du musst den marshaled Speicher kopieren, nicht einfach einen Zeiger auf unallokierten Speicher umerreichen 😉

    MfG



  • /rant/ schrieb:

    LiGERWooD schrieb:

    Talla schrieb:

    Du solltest dich unbedingt entscheiden was du willst. .Net oder nicht .Net. Sonst bekommt man genau das Problem welches du hast, dass man zwischen managed Code und unmanaged Code Typen marshallen muss. Das ist oftmals einfach unnötig, bisher hast du auch noch keinen Grund genannt warum du C++ und .Net Datentypen mischen müsstest.

    ähm diese Marschallmethode hat mir /rant/ gennant, dann wusste er bis dato nicht das ich .Net verwende, ansonsten decke ich mal das du mein eigentliches Problem verstanden hast das ich einen String in char bekommen muss, weil die funktion die es so braucht stammt von einer libary ab, also dahinter steckt assembler, und diese funktion ist in C aber ich brauche diese funktion für einen vorgang im hintergund zu einer .Net Framework anwendung.

    Nun, nicht ganz. Ich habe dir diese Marshaler gebracht, weil ich davon ausgegangen bin, dass du beides (also .NET und nativen Code) verwendest. Ansonsten würden Marshaler in dieser Form ja keinen Sinn ergeben. Du kannst diesen Marshaler benutzen, aber du musst den marshaled Speicher kopieren, nicht einfach einen Zeiger auf unallokierten Speicher umerreichen 😉

    MfG

    sry, nur hab ich überhaupt keine ahnung von Marshal.
    Ist das eine .h oder ein objekt? Und wo finde ich diesen Marshaler überhaupt? Du meinst eine ableitung von Marshaler erstellen, also mit gcnew und so?



  • Es gibt diverse Marshaler. Zum einen hast du die standardmässigen, von .NET bereitgestellten Marshaler von System::Runtime::InteropServices::Marshal sowie diverse Marshaling Attribute (für implizites Marshaling von Argumenten bei DllImport etc.). In Visual C++ (bezogen auf die Professional Version; ob die Aussage auch für die Express Editions gilt, weiss ich nicht) gibt es aber auch noch Header wie msclr/masrshal.h , welche nur mit C++/CLI genutzt werden können - und die unter Umständen wesentlich komfortabler sein können.

    LiGERWooD schrieb:

    Du meinst eine ableitung von Marshaler erstellen, also mit gcnew und so?

    Das kommt darauf an, welchen Typ von Marshaler du benutzen willst. BTW, eine Ableitung erstellen ist nicht dasselbe wie instanzieren (was du mit gcnew , sowie mit einer Deklaration mit auto Semantik tust)...

    MfG



  • /rant/ schrieb:

    Es gibt diverse Marshaler. Zum einen hast du die standardmässigen, von .NET bereitgestellten Marshaler von System::Runtime::InteropServices::Marshal sowie diverse Marshaling Attribute (für implizites Marshaling von Argumenten bei DllImport etc.). In Visual C++ (bezogen auf die Professional Version; ob die Aussage auch für die Express Editions gilt, weiss ich nicht) gibt es aber auch noch Header wie msclr/masrshal.h , welche nur mit C++/CLI genutzt werden können - und die unter Umständen wesentlich komfortabler sein können.

    LiGERWooD schrieb:

    Du meinst eine ableitung von Marshaler erstellen, also mit gcnew und so?

    Das kommt darauf an, welchen Typ von Marshaler du benutzen willst. BTW, eine Ableitung erstellen ist nicht dasselbe wie instanzieren (was du mit gcnew , sowie mit einer Deklaration mit auto Semantik tust)...

    MfG

    Ich verwende Microsoft Visual Studio 2008 C++ und der hate mir auch vorgeschlagen

    #include <msclr/masrshal.h>
    

    und mir einen Beispiel Code gezeigt, allerdings lies sich auch da wieder nicht ein mal das Beispiel compilieren, statesen schleuste er mich in die msclr/masrshal.h an die konfliktzeile. Allerdings ist wohl klar das das Problem ganz wo anders liegt, nämlich warscheinlich an mir. Ich demonstrierie hier noch mal das Beispiel und den Fehler

    // C4996_Marshal.cpp
    // compile with: /clr 
    // C4996 expected
    #include <stdlib.h>
    #include <string.h>
    #include <msclr\marshal.h>
    
    using namespace System;
    using namespace msclr::interop;
    
    int main() {
       String^ message = gcnew String("Test String to Marshal");
       const char* result;
       result = marshal_as<const char*>( message );
       return 0;
    }
    

    Hier meine anpassung

    const char* String_to_char(String^ _String) {
    	String^ message = gcnew String("Test String to Marshal");
    	const char* result;
    	result = marshal_as<const char*>( message );
    	return result;
    }
    
    1>C:\Programme\Microsoft Visual Studio 9.0\VC\include\msclr\marshal.h(203) : error C4996: 'msclr::interop::error_reporting_helper<_To_Type,_From_Type>::marshal_as': This conversion requires a marshal_context.  Please use a marshal_context for this conversion.
    1>        with
    1>        [
    1>            _To_Type=const char *,
    1>            _From_Type=System::String ^
    1>        ]
    1>        C:\Programme\Microsoft Visual Studio 9.0\VC\include\msclr\marshal.h(194): Siehe Deklaration von 'msclr::interop::error_reporting_helper<_To_Type,_From_Type>::marshal_as'
    1>        with
    1>        [
    1>            _To_Type=const char *,
    1>            _From_Type=System::String ^
    1>        ]
    1>        .\AutoMySQL.cpp(62): Siehe Verweis auf die Instanziierung der gerade kompilierten Funktions-template "_To_Type msclr::interop::marshal_as<const char*,System::String^>(const _From_Type &)".
    1>        with
    1>        [
    1>            _To_Type=const char *,
    1>            _From_Type=System::String ^
    1>        ]
    


  • Nun, die Fehlermeldung ist meiner Meinung nach recht eindeutig. Nach const char * zu marshalen lässt eben die Frage offen, wem der Speicher dann gehört. Und dafür brauchst du den marshal_context . Dieser Kontext übernimmt für dich die Verwaltung der marshaled Objekte; wenn der Kontext in seinem Scope ungültig wird, wird der marshaled Speicher frei gegeben. So funktioniert es.

    MfG



  • Mir reieieieieieichtssss!
    Ich habe keine lust mehr!
    Ich habe das gefühl das al diese sachen für eine CLR anwendung sind und in einer Win32 Anwendung sich nicht mit compilieren lassen.

    Denn warum zut teufel funktionieren nicht mal die Beispiele obwohl ich sie in Original zu stand gelassen habe?!

    Warum macht man es hier einem so schwer wen man doch genauso einfach String^ in Integer wandeln kann und es dafür schon Bibliotheken mit den Funktionen gibt?

    ich geh nach hause



  • Nun reg dich doch mal nicht so auf! Marshaling ist für CLR-hybrid Anwendungen, also CLR in Kombinaton mit Win32/Win64. Was ist die konkrete Fehlermeldung, wo tritt sie auf und wann`? Ich bin sicher, dass es sich nur um ein kleines Problem handelt. ⚠

    Marshalling ist halt ein sehr fortgeschrittenes Thema.



  • //String_to_char()
    #include "atlstr.h"
    using namespace System;
    //String_to_char()
    
    char* String_to_char(String^ _String) {
    String ^orig = gcnew String(_String);
        Console::WriteLine("{0} (System::String)", orig);
    
        pin_ptr<const wchar_t> wch = PtrToStringChars(orig);
    
        // Convert to a char*
        size_t origsize = wcslen(wch) + 1;
        const size_t newsize = 100;
        size_t convertedChars = 0;
        char nstring[newsize];
        wcstombs_s(&convertedChars, nstring, origsize, wch, _TRUNCATE);
        strcat_s(nstring, " (char *)");
        cout << nstring << endl;
    
        delete orig;
    
    	return nstring;
    }
    
    1>C:\Programme\Microsoft Visual Studio 9.0\VC\atlmfc\include\atlconv.h(90) : error C3641: "InterlockedExchangePointer": Ungültige Aufrufkonvention "__stdcall " für eine Funktion, die mit /clr:pure oder /clr:safe kompiliert wurde.
    1>C:\Programme\Microsoft Visual Studio 9.0\VC\atlmfc\include\atlconv.h(102) : error C3641: "ATL::_AtlGetConversionACP": Ungültige Aufrufkonvention "__stdcall " für eine Funktion, die mit /clr:pure oder /clr:safe kompiliert wurde.
    1>C:\Programme\Microsoft Visual Studio 9.0\VC\atlmfc\include\atlconv.h(535) : error C3641: "AtlA2WHelper": Ungültige Aufrufkonvention "__stdcall " für eine Funktion, die mit /clr:pure oder /clr:safe kompiliert wurde.
    1>C:\Programme\Microsoft Visual Studio 9.0\VC\atlmfc\include\atlconv.h(554) : error C3641: "AtlW2AHelper": Ungültige Aufrufkonvention "__stdcall " für eine Funktion, die mit /clr:pure oder /clr:safe kompiliert wurde.
    1>C:\Programme\Microsoft Visual Studio 9.0\VC\atlmfc\include\atlconv.h(572) : error C3641: "AtlA2WHelper": Ungültige Aufrufkonvention "__stdcall " für eine Funktion, die mit /clr:pure oder /clr:safe kompiliert wurde.
    1>C:\Programme\Microsoft Visual Studio 9.0\VC\atlmfc\include\atlconv.h(577) : error C3641: "AtlW2AHelper": Ungültige Aufrufkonvention "__stdcall " für eine Funktion, die mit /clr:pure oder /clr:safe kompiliert wurde.
    1>C:\Programme\Microsoft Visual Studio 9.0\VC\atlmfc\include\atlconv.h(530) : error C3641: "AtlDevModeW2A": Ungültige Aufrufkonvention "__stdcall " für eine Funktion, die mit /clr:pure oder /clr:safe kompiliert wurde.
    1>C:\Programme\Microsoft Visual Studio 9.0\VC\atlmfc\include\atltrace.h(65) : error C2440: 'default argument': 'int (__cdecl *)(int,const char *,int,const char *,const char *,...)' kann nicht in 'ATL::CTrace::fnCrtDbgReport_t' konvertiert werden
    1>        Die Adresse einer Funktion liefert die __clrcall-Aufrufkonvention in /clr:pure und /clr:safe. Verwenden Sie evtl. __clrcall im Zieltyp.
    1>C:\Programme\Microsoft Visual Studio 9.0\VC\atlmfc\include\atltrace.h(146) : fatal error C1903: Weiterverarbeitung nach vorherigem Fehler nicht möglich; Kompilierung wird abgebrochen.
    


  • Also,

    wenn du Interoperation im C++/CLI-Stil vor hast, darfst du natürlich nicht mit /clr:pure oder /clr:safe kompilieren, denn dann wird der gute Compiler zu einem C# mit C++/CLI-Syntax kastriert und er kann mit den nativen Elementen nichts mehr anfangen. Setze in den allgemeinen Projekteinstellungen den Schalter "Common Language Runtime support" auf "/clr" und die Fehler bedingt durch Aufrufkonvention ( __clrcall wird bei /clr:pure und /clr:safe forciert, aber das funktioniert natürlich nicht, wenn die referenzierten Funktionen bereits mit einer anderen Konvention kompiliert wurden, wie es beim nativen ATL der Fall ist) und managed/native Interoperation verschwinden von selbst 😉

    Kommen wir nun zu deiner Funktion. Was du gemacht hast, kompiliert und funktioniert (was aber nur Zufall ist) bei mir. Allerdings gibt der Compiler die Warnung " C4172: returning address of local variable or temporary " aus, weil du einen Zeiger auf den Array nstring in Zeile 18 returnst. Nach dem Aufruf der Funktion wird dieser Speicher gekillt und möglicherweise überschrieben. Du solltest besser etwas zurückgeben, was die Verwaltung des Speichers delegiert an jemand anderes. Ich benutze in diesen Fällen meistens einen std::string :

    #include <msclr/marshal.h>
    #include <string>
    
    using namespace msclr::interop;
    using namespace std;
    using namespace System;
    
    string make_string(String ^clr_string)
    {
        marshal_context context;
        string result(context.marshal_as<const char *>(clr_string));
        return result;
    }
    

    Diese Funktionalität, die std::string s zurückgibt, existiert bereits vorgefertigt im Header msclr/marshal_cppstd.h , deshalb kannst du dir die Funktion gleich sparen und die Funktion marshal_as<std::string> aufrufen. Anstelle von Zeigern auf char verwendest du hier eigenständige std::string s, die sich um den Speicher kümmern. Wenn du jetzt irgendwo einen const char * brauchst, dann benutzt du c_str() von std::string . Beispiel:

    #include <cstdio>
    #include <msclr/marshal_cppstd.h>
    
    void c_style_print(const char *what)
    {
        printf("%s", what);
    }
    
    int main()
    {
        String ^Foo = L"ABCDEFG";
        string foo = marshal_as<string>(Foo);
        // Verwendung:
        c_style_print(foo.c_str());
        return 0;
    }
    

    Das scheint dir vielleicht umständlich, aber beim Marshalling muss man an diese Dinge denken. Das ist der Preis, welchen man für die Flexibilität der Sprache bezahlt.

    MfG



  • Also das heißt jetzt etwa das ich einen wert der über marshal ging nicht einer neuen Variable zuweisen kann weil dann beide auf das gleiche zeigen würden?

    Und kann den wert nur noch auslesen lassen etwa mit printf() ?


Anmelden zum Antworten