Es gibt zwei API-Funktionen zum Einlesen der Parameter. Die eine Funktion stellt den gesamten Programmaufruf als Zeichenkette (String) so zu Verfügung, wie er im Konsolmodus eingetippt wurde. Falls das Programm im Windowsmodus gestartet wurde, wird der Eintrag gemeldet, der bei den Programmeigenschaften hinterlegt ist.
In beiden Fällen werden ggf. Gänsefüßchen um den Programmpfad/Programmnamen ergänzt.
Die andere Funktion meldet lediglich die Parameter und deren Anzahl. Wenn man sie benutzt, braucht man die Parameter aus dem Programmaufruf nicht mehr zu extrahieren, denn das ist bereits geschehen. Diese Funktion wird durch dass hier vorgestellte Programm getestet. Es galt, Lücken in der mir vorliegenden Funktionsbeschreibung zu schließen.
Das Testprogramm ist ein wenig provisorisch geschrieben. Man erkennt es an der hier unpassenden Bedienungsanleitung hinter der Fehlermeldung „Unzureichende oder falsche Anzahl Parameter”. Auch die Textausgabe „Hi, I am in the 'main' procedure” deutet an, dass ich mir keine besondere Mühe mit Erstellen des Testprogramms gegeben habe.
Davon abgesehen, habe ich einige Dinge realisiert, die auf der vorhergehenden Webseite erwähnt wurden:
Eine andere Möglichkeit, die Parameter der Kommandozeile auszuwerten, läuft über invoke GetCL. Man findet ein passendes Makro in der Datei /masm32/macros/mascros.asm wenn man nach „GetCL” sucht.
Hi, I am in the 'main' procedure Unzureichende oder falsche Anzahl Parameter Aufruf: FRAGE.exe prompt <key-list> Beispiel: frage.exe "Weitermachen" jn Prompt ggf. in Gänsefüßchen! Press any key to continue ... | Links: Das Program wurde von Explorer aus (d.h., im Windows-Modus) gestartet. Parameter wurden keine mitgegeben. |
0:07:43:59 C:\masm32\Uebung\Konsole>proc Hi, I am in the 'main' procedure Unzureichende oder falsche Anzahl Parameter Aufruf: FRAGE.exe prompt <key-list> Beispiel: frage.exe "Weitermachen" jn Prompt ggf. in Gänsefüßchen! Press any key to continue ... | Links: Das Programm wurde im Konsolmodus gestartet. Parameter wurden keine mitgegeben. Es erscheint die gleiche Fehlermeldung wie im Windows-Modus. |
0:07:44:14 C:\masm32\Uebung\Konsole>proc 1.Argument 2. Argument "3. Argument" '4. Argument' Hi, I am in the 'main' procedure 1.Argument 2. Argument 3. Argument '4. Argument' Press any key to continue ... | |
Der Härtetest erfolgte im Konsolmodus. Das 1.Argument und das 3. Argument wurden so verarbeitet, wie gedacht. Durch den Zwischenraum zwischen 2. und Argument wurden daraus zwei Parameter gemacht. Die einfachen Hochkommata bei '4. Argument' wurden nicht zur Zusammenfassung des Arguments genutzt, so dass auch hier zwei Parameter erkannt wurden. Bei der Einfassung in Gänsefüßchen beim "3. Argument" wurde dies als ein Parameter erkannt. Die Gänsefüßchen wurden von der API-Funktion entfernt. |
; Build this with the "Project" menu using ; "Console Assemble and Link" ; ----------------------------------------------- ; Versuche mit Verarbeitung der Kommandozeile ; ----------------------------------------------- debugFT equ FALSE include c:\masm32\include\mymasm32rt.inc .data cArgument db "A",0,"r",0,"g",0,"u",0,"m",0,"e",0,"n",0,"t",0,0,0 argument db 80 dup (0) cParamFehler db "Unzureichende oder falsche Anzahl Parameter",cr,lf ;Hilfstextanzeige wenn Aufruf ohne Parameter oder nur Spaces als Parameter hilfe db "Aufruf: FRAGE.exe prompt <key-list>",cr,lf db 'Beispiel: frage.exe "Weitermachen" jn',cr,lf db "Prompt ggf. in G",84h,"nsef",81h,0e1h,"chen!" crlf db cr,lf,0 ;Zeilenwechsel ;ergaenzt hilfe comment # Umlauttabelle, enthält Winddows- und MS-Dosumlaute äöüßÄÖÜ jeweils nebeneinander zuerst Windows, dann MS-DOS ä ö ü Ä Ö Ü ß Windows: e4 f6 fc c4 d6 dc df MS-DOS: 84 94 81 8e 99 9a e1 # umlaute db 0e4h,84h,0f6h,94h,0fch,81h,0c4h,08eh ;äöüA db 0d6h,99h,0dch,9ah,0dfh,0e1h ;ÖÜß .data? ;uninitialisierte Daten hInstance HINSTANCE ? pCmdline LPSTR ? pArgs dword ? ;Zeiger auf ein Array iArgs dword ? ;Anzahl Argumente um 1 erhöht .code ;Tell MASM where the code starts ; ----------------------------------------------- start: ; The CODE entry point to the program call main ; branch to the "main" procedure inkey ; Tastendruck abwarten bei Programmende mov eax,2 ; testweise auf Errorlevel 2 exit eax ; ----------------------------------------------- main: print chr$("Hi, I am in the 'main' procedure",13,10) invoke GetCommandLineW ;Verwendung der Unicode-Version mov pCmdline,eax invoke CommandLineToArgvW,eax, ADDR iArgs mov pArgs,eax cmp iArgs,2 ;gibt es mindestens einen Aufrufparameter ? jge zeige_parameter print offset cParamFehler ;wenn nicht, dann Abbruch mit Meldung ret zeige_parameter: mov eax,pArgs ;Startadresse des Arrays nach EAX mov ebx,[eax + 4] ;Startadresse des Aufrufparameters ermitteln push ebx ;Startardesse wird noch benoetigt comment # Statt WideCharToMultiByte() werden nur die zusaetzlichen Nullen entfernt int WideCharToMultiByte( UINT CodePage, // code page DWORD dwFlags, // performance and mapping flags LPCWSTR lpWideCharStr, // address of wide-character string int cchWideChar, // number of characters in string LPSTR lpMultiByteStr, // address of buffer for new string int cchMultiByte, // size of buffer LPCSTR lpDefaultChar, // address of default for unmappable characters LPBOOL lpUsedDefaultChar // address of flag set when default char. used ) Vereinfachter Aufruf mit fester Länge bei cchMultiByte invoke WideCharToMultiByte,CP_ACP,0,ebx,20,offset argument,80,0,0 # mov edi,ebx ;nach mov esi,ebx ;von @@: mov al,[esi] ;fertig konvertiert ? cmp al,0 je @F ;fertig konvertiert ! call todos ;wandelt äöüÄÖÜß von Windows- in DOS-ASCII mov [edi],al ;zusammenschieben inc edi inc esi inc esi jmp @B @@: mov [edi],al ;Stringende markieren pop ebx ;Startadresse des Parameters print ebx ;entspricht print offset argument print offset crlf dec iArgs add pArgs,4 cmp iArgs,1 jne zeige_parameter ;zur Anzeige des nächsten Parameters ret ;nun sind alle Parameter abgearbeitet ; Unterprogramme ; --------------------------------------------------- ;Unterprogramm Codewandlung Umlaut von Windows- in DOS-Codierung (ASCII) ;Das zu untersuchende Zeichen steht in al ; ----- todos: mov cx,7 ;Tabelle Umlaute ist 14 Zeichen lang lea ebx, umlaute ;zur Adressierung .while cx >0 cmp [ebx],al je @F inc ebx ;adressieren naechsten Tabellenplatz inc ebx dec cx .endw ret @@: inc ebx ;Codewandlung des Zeichens und mov al,[ebx] ;Unterprogrammende ret ; ----------------------------------------------- end start ; Tell MASM where the program ends
argument unter .data: Das 80 Byte lange Feld wird nicht verwendet. Es würde Verwendung finden, wenn man die API-Funktion WideCharToMultiByte zur Wandlung von Unicode in Windows-ASCII genutzt hätte. Ein solcher Aufruf wird weiter unten im Quelltext als Kommentar beschrieben.
Anzeigetexte unter .data: Ab cParamFehler schließt sich der Text eines Fehlerhinweises mit einem anschließenden Benutzungshinweis an. Die beiden folgen Labels sind so gesetzt, dass auch nur der Benutzungshinweis oder nur ein Zeilenwechsel ausgegeben werden kann.
umlaute ist eine Tabelle. Sie ermöglicht die Übersetzung der deutschen Umlaute von Windows- in MS-DOS-Codierung. Die Tabelle wird von dem Unterprogramm todos genutzt.
hInstance unter .data? wird nicht genutzt, denn dies Programm verwendet keine zusätzlichen Handles. Die Datei /masm32/include/windows.inc stellt über das dort definierte Schlüsselwort sicher, dass hInstance ein Doppelwort (dword) ist.
pArgs und iArgs unter data? gehören zum Aufruf von GetCommandLineW.
Bei start unter .code beginnt das vier Befehle kurze Hauptprogramm. Es handelt sich um den Aufruf des Unterprogramms main. Nach der Rückkehr daraus wird mit Inkey eine Texteingabe angefordert. Dadurch wird erreicht, dass das Programm erst nach einer zusätzlichen Tastenbetätigung beendet wird. Würde das Programm unter Windows gestartet, wäre es ohne eine solche Zwangsbremse so schnell durchgelaufen, dass der Benutzer es auf dem Bildschirm nicht gesehen hätte. Testweise wird das Programm mit dem Errorlevel 2 beendet.
Das Unterprogramm main führt die Verarbeitung durch. Zuerst wird aus Testgründen die Zeile „Hi, I am in the 'main' procedure” auf den Bildschirm geschrieben. Danach wird mit der API-Funktion GetCommandLineW die Adresse der Kommandozeile, mit der das Programm gestartet wurde, ermittelt und im Doppelwort unter pCMDline gespeichert. Tatsächlich hätte man in diesem Programm auf diese Speicherung verzichten können, denn pCMDLine wird im Programm an keiner weiteren Stelle verwendet!
Der anschließende Aufruf der API-Funktion CommandLineToArgvW gibt in EAX die Startadresse der Kommandozeile zurück. Sie wird in pARGs gesichert. Die Anzahl der Parameter hatte die API-Funktion eigenständig in iArgs eingetragen.
CommandLineToArgvW zählt den Programmaufruf als ersten Parameter. Wenn nur ein weiterer Parameter angegeben wird, stünde der Zähler auf 2. Sofern kein Parameter angegeben wurde, würde der unter .data ab cParamFehler hinterlegte Text auf den Bildschirm gezeigt werden und das Unterprogramm main würde vorzeitig verlassen werden.
Andernfalls wird ab zeige_parameter die zeilenweise Anzeige der Parameter auf dem Bildschirm vorbereitet. Der Programmaufruf (ggf. Pfad, auf jeden Fall aber Programmname) soll nicht angezeigt werden. Deshalb wird der im Register EBX aufgebaute Adresszeiger gleich um Doppelwortlänge (4 Bytes) vorgerückt. Danach zeigt er auf den ersten Parameter der Kommandozeile. Da sein Wert später erneut benötigt wird, wird EBX im Stack gesichert.
Der mit comment # startende Kommentar zeigt, mit welcher API-Funktion nacheinander jeder einzelne Parameter von Unicode-16 in Windows-ASCII gewandelt werden kann. Allerdings ist dies Programm keine Windows-Anwendung, sondern es ist eine Konsolanwendung. Nach der Codewandlung mit der API-Funktion hätte man den entstandenen Windows-ASCII-Text wegen der deutschen Umlaute in DOS-ASCII wandeln müssen. Ich habe deshalb auf die Verwendung der API-Funktion WideCharToMultiByte verzichtet und die Codewandlung von Unicode-16 in DOS-ASCII in den 13 Programmzeilen (Leerzeilen mitgezählt) hinter dem Kommentar untergebracht. Ohne den Unterprogrammaufruf call todos wird jedes zweite Zeichen der Unicode-16-Codierung (also die 0) entfernt und die Zeichenkette wird nach links aufgeschoben. Es passiert also:
db "T",0,"e",0,"d",0,"d",0,"y",0,0,0 wird zu db "T","e","d","d","y",0,"d",0,"y",0,0,0
Da bei den Strings die erste auftauchende Null als Stringende betrachtet wird, gilt der umgewandelte String unmittelbar hinter dem Wort „Teddy” als beendet.
Mit dem call todos wird das sich gerade behandelte einzelne Zeichen überprüft, ob es ein Umlaut in Windows-ASCII ist. Falls ja, wird es in DOS-ASCII gewandelt.
Mit pop ebx wird die Startadresse des Parameters wieder hervorgeholt und der Parameter wird sofort anschließend als Zeile auf dem Bildschirm dargestellt. Sofern weitere Parameter vorhanden sind, wird mit der Bearbeitung des nächsten Parameters begonnen. Andernfalls wird das Unterprogramm main hier verlassen.
Der Code, den ich etwas locker als Windows-ASCII bezeichne, heißt offizieller „Windows-Codepage 1252 (oder cp1252, Windows-1252, …). Für die meisten Zeichen kann man von Windows-1252 in Unicode-8 (UTF-8) dadurch übersetzen, dass man ein Nullbyte voranstellt. Beispielsweise wird das große A in Windows-1252 als db 40h codiert. In UTF-8 lautet seine Codierung dw 0040h oder alternativ db 40h,0.
Bei einigen Sonderzeichen im Windows-1252-Bereich db 80h bis db 9fh gibt es Ausnahmen. Glücklicherweise sind die Codierungen der deutschen ÄÖÜäöüß hiervon nicht betroffen, so dass man einfach eine 0 ergänzen oder entfernen muss. Betroffen ist beispielsweise das Währungszeichen €. In Windows-1252 ist es db 80h, bei UTF-8 ist es dw 20ach bzw. db 0ach,20h.
Eine gut brauchbare Übersetzungstabelle zwischen Windows-1252 und UTF-8 ist auf →www.recherche-redaktion.de/zeichensaetze/cp1252.htm zu finden.
Dieser Bildschirmabdruck zeigt die DOS-ASCII-Codetabelle.
Das Unterprogramm todos führt die Codewandlung der deutschen Umlaute ÄÖÜäöüß von Windows-1252 in DOS-ASCII durch. Es ist erstaunlich einfach.
Besonders sei angemerkt, dass die sonst von der 16-bit Assemblierung gewohnte ähnliche Schleifenbildung mit
mov cx,Schleifenzähler Schleifenanfang: … sonstige befehle … loop Schleifenanfangunter der 32-bit-Programmierung nicht funktioniert. Bei der 32-bit-Programmierung arbeitet loop mit dem ecx-register zusammen! Man müsste hier codieren:
mov ecx,Schleifenzähler Schleifenanfang: … sonstige befehle … loop Schleifenanfang