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''.mp3
    

    Bitte beachten, dass die Datei ''40''.mp3 und nicht etwa "40".mp3 heiß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 der main() Funktion auswertet, kommt dort nur folgender String an:

    D:\TEST''40''.mp3
    

    Der 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 die main() 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 einer main() 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 normale main() 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 eigentliche main() auf. Da aber WinMain() die Kommandozeile "am Stück" bekommt, muss sich "qtmain.lib" offensichtlich selbst die Argumente erparsen, um meine main() 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 die main() ja ganz normal direkt aufgerufen...

    Wenn ich argc und argv[] einfach ignoriere und mir die Argumente selbst mit CommandLineToArgvW() erparse, ist alles okay. *seufz*


Log in to reply