Ich bin derzeit ein wenig mit C++ am lernen. Ich verstehe einfach nicht, warum es X tausende „Datentypen“ wie string, LWCSTR, LPWSTR und was es nicht alles gibt und wie man diese konvertieren kann.
Gut, ein wenig hab ich in den letzen Stunden schon gelernt So wie ich das nun verstanden habe ist LPWSTR ANSI und LPWCSTR Unicode.
Ich habe ein NPAPI Plugin geschrieben was eine EXE ansprechen soll. Die Executable ist erstmal nur ein Platzhalter und soll einfach die Parameter in einer MessageBox ausgeben.
Hier die Executable:
/* convert/cast LPTSTR to LPCWSTR */
LPCWSTR test = (LPCWSTR) lpCmdLine;
/* Print out the arguments */
MessageBox(NULL, test, TEXT("World of RPG"), NULL);
/* Exit with return code 0x00 */
return 0;
}```
Wenn ich die Applikation nun direkt über Visual Studio mit den Parameter "-console" ausführe, wird die MessageBox korrekt ausgegeben:
Jetzt versuche ich die Exe über mein NPAPI-Plugin auszuführen:
```/* Javascript call: document.getElementById("myPlugin").startGame("-console"); */
std::string WoRPGAPI::startGame(const std::string& method_args) {
/* Define JSON result variables */
FB::VariantMap json;
Json::StyledWriter writer;
/* Define process variables*/
PROCESS_INFORMATION processInformation = { 0 };
STARTUPINFO startupInfo = { 0 };
startupInfo.cb = sizeof(startupInfo);
/* Convert from method argument "method_args" string to LPWSTR */
LPWSTR game_args = new wchar_t[method_args.size() + 1]; // +1 for zero at the end
copy(method_args.begin(), method_args.end(), game_args);
game_args[method_args.size()] = 0; // zero at the end
/* Create the Process */
BOOL status = CreateProcess((LPWSTR) LgetGamePath("World of RPG.exe").c_str(), game_args, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInformation);
/* Check the process status */
if (!status) {
DWORD dLastError = GetLastError();
LPCTSTR strErrorMessage = NULL;
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ARGUMENT_ARRAY | FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL, dLastError, 0, (LPWSTR)&strErrorMessage, 0, NULL);
json["code"] = dLastError;
json["message"] = strErrorMessage;
}
json["status"] = status;
/* get JSON back to the browser */
return writer.write(FB::variantToJsonValue(json));
}```
Und hier ist das Resultat:
Schaut ziemlich Leer aus. Ok, testen wir des nun mal mit einem langen String (startGame("This is a longer string!")):
Komischerweise wird da das erste Wort immer weggelassen.
Kann mir jemand sagen, was ich da falsch mache und wie ich korrekt von std::string zu LPWSTR konvertieren kann?
Ich hab jetzt schon alles erdenkliche herumprobiert, nur will das ganze irgendwie nicht bei mir. Ich hab auch schon durch längeres Googeln einige snippets gefunden gehabt, die funktionieren aber auch nicht wirklich. Entweder wird der Text dann Cryptisch angezeigt (Chinesische Zeichen), ich denke mal das des an Unicode liegt, oder das Compilen schlägt fehlt weil der bei irgendetwas nicht "Casten" kann.
Weil es X tausende Compiler und X tausende CPU-Architekturen gibt. LWCSTR ist eine typedef für einen typdef für einen $WHATEVER. Immer Grunde kannst Du Dich mal durch die Header klicken und schauen wie das alles aufgebaut ist (rein zur Information, musst nur das Prinzip verstehen).
Bestes Beispiel ist UINT32. UINT32 wie der Name schon vermuten lässt Vorzeichlos 32 Bit. int selber ist als 2 oder 4 Bytes definiert, das ist aber Compiler abhängig. Ich habe jetzt nicht den Header im Kopf der UINT32 definiert. Aber jeder Compiler selber bringt diesen speziellen Header mit, da er Compiler abhängig ist. Wenn du jetzt eine Methode hast void foo(UINT32 value) ist unter jedem Compiler (der sich an den Standard hält) sicher gestellt das value immer 32 Bit breit ist. Über void foo(int value) ist das nicht so (da int 2 oder 4 Byte sein kann).
ARM definiert für den SHARC-Prozessor bool, char, short, int und long alles als 32 Bit (der Standard defniert nur eine Reihenfolge der Breiten pro Datentyp [bool <= char <= short <= int <= long])
Du musst beim Casten auf die Bit-Ebene des Prozessors runter (das ist etwas was man in Java eher selten muss).
Du hast folgenden C-Str: „4321“, dann steht im Speicher 0x34 . 0x33 . 0x32 . 0x31 . 0x00. Wichtig ist erstmal das 0x00 am Ende, das signalisiert den C-Methoden das hier der String zu Ende ist. Wenn Du jetzt den Cast mauf (LPWSTR) machst, sagt Du dem Compiler „betrachte den Speicher jetzt nicht mehr als C-Str-Anordnung, sondern als Unicode Anordnung“. Damit „sieht“ der Compiler folgendes: 0x34 0x33 . 0x32 0x31 . 0x00 0x??. Im Grund hast Du nun zwei gravierende Probleme. Zum Einen statt der Zahlen zwei Unicodezeichen: 0x3433 und 0x3231 (was das jetzt ist, weis ich nicht). Zum Anderen hast Du kein Ende der Zeichenkette mehr, denn an die Stelle von 0x?? rück genau das was da im Speicher steht (eine Chance von 1:255 das es genau 0x00 ist). Im Zweifelsfall hast Du plötzlich keine Zeichenkette mit 4 Zahlen mehr, sondern eine unbekannte Anzahl an Unicode-Zeichen.
dann gleich zwei wichtige Hinweis für Dich:
[ol]
[li]komischen Verhalten des Programmflusses zeugt von Bufferoverflow und fehlerhaften Casting
[/li][li]Compiler-Warnung IMMER als Fehler betrachten und gleich korrigieren
[/li][/ol]
gerade Letzteres ist wichtig. Eine Warnung des Compilers ist ein Hinweis an Dich das hier durchaus nicht das rauskommen könnte, was Du willst. Meistens kann man das mit einem Cast eliminieren, aber da - vorher - nach denken ob der Cast auch richtig ist (vgl. obiges Beispiel)
CreateProcess benötig einen C-Str, da brauchst Du nix casten. Im Zweifelsfall erstmal eine MessageBox mit der Zeichenkette öffnen (printf-Debug).
du solltest entweder durchgängig mit UNICODE- oder durchgängig mit ANSI-Strings arbeiten. Dieses Mischmasch und das damit verbundene Konvertierungskuddelmuddel führt zwangsläufig zu Problemen, wenn man nicht genau weiß, was man tut.
Wenn du UNICODE verwenden willst, dann benutze ‘std::wstring’ statt ‘std::string’.
Wenn du (in einem UNICODE-Projekt) mit ANSI-Strings arbeiten willst, dann benutze die ANSI-Implementierungen der API-Funktionen. In deinem Fall wäre das dann “CreateProcessA()”.
Falls doch mal eine Umwandlung nötig ist, funtioniert das recht zuverlässig mit den Funktionen ‘mbstowcs()’ (ANSI -> UNICODE) und ‘wcstombs()’ (UNICODE -> ANSI).