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 selbstKommen 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 Arraynstring
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 einenstd::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 Headermsclr/marshal_cppstd.h
, deshalb kannst du dir die Funktion gleich sparen und die Funktionmarshal_as<std::string>
aufrufen. Anstelle von Zeigern aufchar
verwendest du hier eigenständigestd::string
s, die sich um den Speicher kümmern. Wenn du jetzt irgendwo einenconst char *
brauchst, dann benutzt duc_str()
vonstd::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 anderenstd::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 welchesstrlen
bzw.wcslen
mappt;_tcscpy_s
wird zustrcpy_s
oderwcscpy_s
, welches zustrcpy
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
-