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



  • 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() ?



  • LiGERWooD schrieb:

    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() ?

    Du kannst den Rückgabewert von marshal_as<std::string> wie jeden anderen std::string benutzen. Und deine zweite Frage verstehe ich nicht, sorry 😉



  • AutoMySQL.cpp

    #include <cstdio>
    #include <msclr/marshal_cppstd.h>
    using namespace msclr::interop;
    using namespace std;
    using namespace System;
    

    AutoMySQL.h

    using namespace System;
    
    ref class AutoMySQL
    {
    	static bool verbunden = false;
    
    public:
    	String^ CREATE_TABLE(String^ Name,String^ Felder);
    	String^ MySQL();
    	const char* String_to_char(String^ _String);
    };
    

    AutoMySQL.cpp

    const char* String_to_char(String^ _String) {
    	String ^Foo = L"ABCDEFG";
    	string foo = marshal_as<string>(Foo);
    	mysql_query(my, foo.c_str());
    	return "";
    }
    

    Ausgabe

    1>AutoMySQL.obj : error LNK2020: Nicht aufgelöstes Token (06000015) AutoMySQL::String_to_char.
    1>El Em Ci.obj : error LNK2020: Nicht aufgelöstes Token (06000008) AutoMySQL::String_to_char.
    1>D:\Programmierung\Visual Studio 2008\Projekte\El Em Ci\Debug\El Em Ci.exe : fatal error LNK1120: 2 nicht aufgelöste externe Verweise.
    


  • Also liebe leute die mir bis jetzt weitergeholfen haben, ich bin jetzt noch mal auf http://www.codeguru.com/cpp/cpp/cpp_managed/interop/print.php/c14063__1/ zurück und habe noch mal dieses Beispiel ausbrobiert. Abgesehen davon das ich es endlich geblickt habe das man erstmal sagen muss das marshal_context() von msclr::interop:: kommt, kennt er nicht WCHAR, _T, _tcslen, _tcscpy_s

    #include <msclr\marshal.h>
    
    using namespace msclr::interop;
    
    int main(void) {
    	TCHAR* c_style_string = _T("My C style string");
    	System::String^ dotNetString = msclr::interop::marshal_as<System::String^>(c_style_string);
    
    	//declare new marshal_context
    	marshal_context^ mc = gcnew marshal_context();
    
    	//convert string from .NET to C-style
    	const TCHAR* new_c_style_string = mc->marshal_as<const TCHAR*>(dotNetString);
    
    	//get the length of the convert string
    	int strLen = (int)_tcslen(new_c_style_string) + 1;
    
    	//allocate a new character array to hold the string
    	TCHAR* copy_of_new_c_style_string = new TCHAR(strLen);
    
    	//copy to the new array
    	_tcscpy_s(copy_of_new_c_style_string, strLen, new_c_style_string);
    
    	//delete the marshaling context
    	delete mc;
    
    	return 0;
    }
    
    1>.\main.cpp(6) : error C3861: "_T": Bezeichner wurde nicht gefunden.
    1>.\main.cpp(16) : error C3861: "_tcslen": Bezeichner wurde nicht gefunden.
    1>.\main.cpp(22) : error C3861: "_tcscpy_s": Bezeichner wurde nicht gefunden.
    

    Ich bitte jetzt noch mal um eure Hilfe und aufklärung.

    Laut ein paar seiten im web heißt es _tcslen stamme von string.h habe auch eingebunden #include <string.h> ging aber trotzdem nicht.



  • Hmm okay. Dieses _T differenziert String-Literale anhand der Compiler Einstellungen. Wenn du z.B. für ein Wide Character Set kompilierst, dann wird aus _T("Hallo, Welt") L"Hallo, Welt" . Wenn nicht, dann wird daraus nur "Hallo, Welt" . Falls du diesen Unterschied nicht verstehst, ist das nicht weiter tragisch. Die Verwendung dieser Makros erlaubt dir, beides zu benutzen, ohne den Code zu ändern. _tcslen ist ein Makro welches strlen bzw. wcslen mappt; _tcscpy_s wird zu strcpy_s oder wcscpy_s , welches zu strcpy aquivalent ist. Darum ist dein Code in Wirklichkeit:

    #include <msclr\marshal.h>
    
    using namespace msclr::interop;
    
    int main(void) {
        const char* c_style_string = "My C style string";
        System::String^ dotNetString = msclr::interop::marshal_as<System::String^>(c_style_string);
    
        //declare new marshal_context
        marshal_context^ mc = gcnew marshal_context();
    
        //convert string from .NET to C-style
        const char* new_c_style_string = mc->marshal_as<const char*>(dotNetString);
    
        //get the length of the convert string
        int strLen = (int)strlen(new_c_style_string) + 1;
    
        //allocate a new character array to hold the string
        // EDIT: die Klammer nach new char allokiert im Beispiel auf CodeGuru nur ein char, nicht einen Array. Also ein Fehler...
        char* copy_of_new_c_style_string = new char[strLen];
    
        //copy to the new array
        strcpy(copy_of_new_c_style_string, strLen, new_c_style_string);
    
        //delete the marshaling context
        delete mc;
    
        return 0;
    }
    


  • Also das TCHAR* char* sein sol und _T L"" habe ich mir schon fast gedacht und das diese _ funktionen unter anderem auf strLen verweisen. Aber jetzt habe ich da ein Fehler bei Z 23 und zwar strcpy verlangt 2 übergabeparamter des types char. Also ich denke strcpy ist die alte unsichere variante und du hast aber schon an die neue gedacht nur dich verschrieben. So wie bei strcat zu strncat

    Also so

    strncpy(copy_of_new_c_style_string, new_c_style_string, strLen);
    

    oder? 🙂 ➡



  • Ja, probiers doch aus. Ich benutze sowieso keine dieser Funktionen mehr 😉




Anmelden zum Antworten