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



  • //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