Bug oder Feature: C++ Runtime lässt Backslash aus CLI Argument verschwinden [gelöst]
-
Hallo.
Mein Programm verwendet folgende CLI Syntax:
Program.exe --add <Dateipfad>Probleme treten auf, wenn folgende Datei geöffnet werden soll:
D:\TEST\''40''.mp3Bitte beachten, dass die Datei
''40''.mp3und nicht etwa"40".mp3heißt (letzteres wäre kein gültiger Dateiname).Damit ergibt sich die Kommandozeile folglich zu:
Program.exe --add "D:\TEST\''40''.mp3"Allerdings parst das C++ Runtime (Visual C++ 2010) diese Kommandozeile offensichtlich falsch !!!
Wenn man das
argv[]Parameter dermain()Funktion auswertet, kommt dort nur folgender String an:D:\TEST''40''.mp3Der Backslash ist verschwunden! Und natürlich kann die Datei deshalb nicht korrekt geöffnet werden.
Siehe auch:
http://postimage.org/image/f3kikz26h/full/Ist das ein Bug oder ein "Feature"? Und wie lässt sich dieses Problem umgehen?
Natürlich könnte man sich die Kommandozeile via
GetCommandLineW()holen und "von Hand" parsen. Aber das ist nun wirklich nich schön...Vielen Dank für hilfreiche Hinweise...
-
Bug kann ich mir grad nicht vorstellen. Eher ein etwas unpraktisches Feature, damit man ' und " in Argumenten haben kann.
Was natürlich doof ist, wenn man Filenamen unverändert übergeben will.ps:
http://msdn.microsoft.com/en-us/library/windows/desktop/bb776391.aspx
CommandLineToArgvW has a special interpretation of backslash characters when they are followed by a quotation mark character ("), as follows:
[]2n backslashes followed by a quotation mark produce n backslashes followed by a quotation mark.
[](2n) + 1 backslashes followed by a quotation mark again produce n backslashes followed by a quotation mark.
[*]n backslashes not followed by a quotation mark simply produce n backslashes.Steht jetzt nur " da, aber vermutlich gilt für ' das selbe.
Und mit einem backslash wird die "(2n) + 1" greifen, wobei n dann 0 ist.ps2:
Hier nochmal was zu dem Thema:
http://msdn.microsoft.com/en-us/library/a1y7w461.aspx
Steht aber wieder nur " da, steht sogar explizit double quotation mark (").
Seltsam...
-
Hmm, Du hast recht. Auch wenn es anscheinend nicht dokumentiert ist, kann man Kommandozeilen-Argumente, die Leerzeichen enthalten, auch mit**
'anstatt"umschließen. Hab es gerade ausprobiert. Das ist zwar fehleranfällig, da ja'Zeichen auch innerhalb von Pfaden auftreten können, aber nehmen wir das mal so hin. Dann macht es durchaus Sinn, dass man'Zeichen durch voranstellen eines Backslash escapen kann, um ein'Literal zu erzwingen - allerdings nur innerhalb von Argumenten die tatsächlich von'Zeichen umschlossen sind. Wenn das Argument hingegen von"Zeichen umschlossen ist (wie es wohl in 99,9% der Fälle sein wird), dann müsste ein\'**doch auch genau so erhalten bleiben. Das Escapen wäre in diesem Fall ja gar nicht notwendig...
-
Ja ist alles etwas komisch.
Du kannst ja mal CommandLineToArgvW probieren, vielleicht tut das besser.
Oder du schreibst dir halt selber ne kleine Hilfsfunktion. Lästig, aber sollte in ein paar Minuten erledigt sein.
-
Update:
Seltsamerweise sind die CLI Argumente nur dann "kaputt", wenn ich die Binary als "Windows" (GUI) kompiliere. Als "Console" ist alles wie erwartet.
Allerdings will ich ja nicht, dass meine GUI Anwendung mit Konsole startet...
-
Hm. Sehr interessant.
Aber versuch' wirklich mal CommandLineToArgvW() zu verwenden. Ist ja denkbar einfach.
-
hustbaer schrieb:
Ist ja denkbar einfach.
Ja und Nein.
Eigentlich greife ich ja über Qt mittels
QApplication::arguments()auf die Argumente zu.Dachte auch zuerst, dass es irgendwie an Qt liegt. Aber es zeigt sich, dass das Problem schon direkt in der
main()Methode besteht.Anscheinend übernimmt Qt also einfach das
argv[]Array so wie es vom C++ Runtime in diemain()Methode rein kommt...
-
Eventuell sieht auch die Windows-Shell das Ding schon als Escape-Charakter?
-
pumuckl schrieb:
Eventuell sieht auch die Windows-Shell das Ding schon als Escape-Charakter?
Also laut ProcessExplorer ist die Kommandozeile, mit der mein Programm aufgerufen wird, ja noch "korrekt".
Und wie gesagt, als "Console" kompiliert sind die Argumente, die in die
main()rein kommen auch noch so wie erwartet. Nur als "Windows" (GUI) Binary eben nicht mehr.Es muss also beim Parsen der Kommanozeilen-Argumente passieren. Und das passiert ja schon vor der
main(), sprich irgendwo in der Visual C++ Runtime.Wie es aussieht werde ich mir tatsächlich die "original" Kommandozeile mit
GetCommandlineW()holen und "von Hand" parsen müssen. So viel zur Portierbarkeit. Argh!
-
Die Lösung:
Okay, Ich habe des Rätsels Lösung gefunden! Wenn man ein Programm als "Windows" (GUI) kompiliert, dann benötigt man ja eine
WinMain()anstelle einermain()Funktion - sonst gibt's hässliche Linker-Fehler! Das lässt sich aber mit Qt durch Hineinlinken von "qtmain.lib" beheben. Dann kann man auch in einer GUI Anwendung wieder eine normalemain()haben.Hatte mir bisher keine Gedenken darüber gemacht, wie das "intern" funktioniert. Aber anscheinend implementiert "qtmain.lib" einfach eine Dummy-
WinMain()(um den Linker glücklich zu machen) und ruft von dort aus meine eigentlichemain()auf. Da aberWinMain()die Kommandozeile "am Stück" bekommt, muss sich "qtmain.lib" offensichtlich selbst die Argumente erparsen, um meinemain()mit argc und argv[] aufrufen zu können. Und da liegt der Hund begraben!!!Callstack:
WinMainCRTStartup() -> WinMain() -> main()Auch wenn es also alles andere als offensichtlich ist, kommen hier die Aufrufparameter von
main()nicht direkt aus dem C++ Runtime sondern irgendwo aus der Qt Library. Das erklärt auch, wieso bei einer "Console" Anwendung kein Problem vorhanden war. Da wird diemain()ja ganz normal direkt aufgerufen...Wenn ich argc und argv[] einfach ignoriere und mir die Argumente selbst mit
CommandLineToArgvW()erparse, ist alles okay. *seufz*