Home

Testprogramm zum Einlesen
der Programmaufrufparameter

Die Messagebox MSGbox in
Visual BASIC 2010 und in Masm32

MASM32-
Infoseite

MASM32 SDK (Windows Konsole):
Programmbeispiel: Frage-Programm und ein Vergleich mit PureBasic

Dies ist ein 32-bit-Nachbau des 16-bit-Programms  i FRAGE.COM.

Beide Versionen leisten etwa das gleiche. Allerdings unterscheiden sie sich etwas im Format des Aufrufs.

Die hier gezeigte 32-bit-Version baut auf dem Testprogramm zur Parameterabfrage auf. Das Testprogramm wurde auf der vorhergehenden Webseite beschrieben.

Das 32-bit-Programm Frage.exe ist 3072 Byte groß. FRAGE.COM ist mit 216 Bytes Größe deutlich kleiner.

Für eine weiteren Vergleich habe ich ein funktionsgleiches Programm in der Programmiersprache PureBasic erstellt. Das daraus ertellte ausführbare 32-bit-Programm Frage.exe kommt auf 12899 Bytes Größe.

Die Funktion von Frage.exe lässt sich anhand der Steuerdatei Ask.bat demonstrieren:

if %1 == 2 goto frage2
if %1 =  1 goto frage1
Frage "Bitte 'J' zur Antwort Ja" nj
goto returncode

:frage1
Frage "Nur ein Hinweis!"
goto returncode

:frage2
Frage

:returncode 
if errorlevel 99 goto aufruffehler
if errorlevel 2 goto ja
if errorlevel 1 goto nein

echo Aufruf mit nur einem Parameter 
goto raus

:aufruffehler
echo Der Aufruf war fehlerhaft
goto raus 

:ja
echo Die Antwort war "Ja"
goto raus

:nein
echo Die Antwort war "Nein"
:raus
Ablaufbeispiel
Links wird die Scriptdatei Ask.bat gezeigt. Unterhalb ist die etwas abgeschnittenen Bildschirmanzeige von vier Durchläufen zu sehen.

0:05:25:29 C:\masm32\Uebung\Konsole>ask
Bitte 'j' zur Antwort Ja>
Press any key to continue ...
Die Antwort war "Ja"

0:05:25:57 C:\masm32\Uebung\Konsole>ask
Bitte 'j' zur Antwort Ja>
Press any key to continue ...
Die Antwort war "Nein"

0:05:26:03 C:\masm32\Uebung\Konsole>ask 1
Nur ein Hinweis!
Press any key to continue ...
Aufruf mit nur einem Parameter

0:05:26:08 C:\masm32\Uebung\Konsole>ask 2
Aufruf nur mit einem oder zwei Parametern möglich!

Aufruf:   FRAGE.exe prompt [key-list]
Beispiel: frage.exe "Weitermachen (jn)" jn
Ohne key-list kann mit einer beliebigen Taste gean
Prompt ggf. in Gänsefüßchen!

Press any key to continue ...
Der Aufruf war fehlerhaft

0:05:26:16 C:\masm32\Uebung\Konsole>
Das Quellprogramm für MASM32 SDK
Eine kurze Interpretation der Unterschiede zwischen diesem Programm und dem Testprogramm zur Parameterabfrage steht unterhalb des Quellprogrammtextes. Das Testprogramm zur Parameterabfrage wurde auf der vorhergehenden Webseite beschrieben.

;   Build this with the "Project" menu using
;          "Console Assemble and Link"
; -----------------------------------------------
;   Versuche mit Verarbeitung der Kommandozeile 
; -----------------------------------------------
    .586                                    ; create 32 bit code
    .model flat, stdcall                    ; 32 bit memory model
    option casemap :none                    ; case sensitive

.XLIST 
    include \masm32\include\windows.inc     ; always first
    include \masm32\macros\macros.asm       ; MASM support macros

; include files that have MASM format prototypes for function calls
; -----------------------------------------------------------------
    include \masm32\include\masm32.inc
    include \masm32\include\gdi32.inc
    include \masm32\include\user32.inc
    include \masm32\include\kernel32.inc
    include c:\masm32\include\shell32.inc
    include c:\masm32\include\msvcrt.inc
  
; Library files that have definitions for function
; exports and tested reliable prebuilt code.
; ------------------------------------------------
    includelib \masm32\lib\masm32.lib
    includelib \masm32\lib\gdi32.lib
    includelib \masm32\lib\user32.lib
    includelib \masm32\lib\kernel32.lib
    includelib c:\masm32\lib\shell32.lib
    includelib c:\masm32\lib\msvcrt.lib
.LIST

; Konstanten und Macros
; ------------------------------------------------
bel     equ     7       ;Klingel
bs	equ     8       ;Backspace
ht	equ	9       ;Horizontaler Tabulator
lf      equ     0ah	;Linefeed
vt	equ	0bh	;Vertikaler Tabulator
ff	equ	0ch	:Formfeed
cr      equ     0dh	;Carriage Return
Ae      equ     8eh     ;Ä DOS-ASCII-codiert    
Oe      equ     99h     ;Ö
Ue      equ     9ah     ;Ü
ae      equ     84h     ;ä
oe      equ     94h     ;ö
ue      equ     81h     ;ü
sz      equ     0e1h    ;ß

.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  "Aufruf nur mit einem oder zwei Parametern m",oe,"glich!",cr,lf,lf
;Hilfstextanzeige wenn Aufruf ohne Parameter
hilfe   db      "Aufruf:   FRAGE.exe prompt [key-list]",cr,lf
        db      'Beispiel: frage.exe "Weitermachen (jn)" jn',cr,lf
	db	"Ohne key-list kann mit einer beliebigen Taste geantwortet werden",cr,lf 	
        db      "Prompt ggf. in G",ae,"nsef",ue,sz,"chen!",lf
crlf    db      cr,lf,0         ;Zeilenwechsel  ;ergaenzt hilfe
prompt  db      '>',0           ;fast ein Promptzeichen

comment #
Umlauttabelle, enthält Windows- 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,ae,0f6h,oe,0fch,ue,0c4h,Ae ;äöüA 
        db 0d6h,Oe,0dch,Ue,0dfh,sz         ;ÖÜß

.DATA?		        ;uninitialisierte Daten
pCmdline        LPSTR   ?        
pArgs           dword   ?       ;Zeiger auf ein Array
pArgsdup        dword   ?       ;pArgs gesichert
iArgs           dword   ?       ;Anzahl Argumente +1
iArgszaehler    dword   ?       ;Schleifenzaehler Anzahl Argumente              


.code                   ; Tell MASM where the code starts

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

start:                          ; The CODE entry point to the program
        call    main            ; branch to the "main" procedure
        
        push    eax             ; sichert den ermittelten Errorlevel 
        inkey                   ; Tastendruck abwarten bei Programmende
        pop     eax
        exit    eax             ;Errorlevel wurde in eax mitgegeben

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««


main:   invoke  GetCommandLineW         ;Verwendung der Unicode-Version
        mov     pCmdline,eax

        invoke  CommandLineToArgvW,eax, ADDR iArgs
        mov     pArgs,eax
        mov     pArgsdup,eax

        mov     eax,iArgs                       ;iArgs enthält Parameteranzahl +1
        .if     (eax != 2 && eax != 3)          ;Parameteranzahl muss 1 oder 2 sein     
                print   offset  cParamFehler    ;falsche Parameteranzahl  
                mov     eax,99                  ;Errorlevel 99 und Schluss!         
                ret 
        .endif
      
        dec     eax
        mov     iArgszaehler,eax  ;Schleifenzaehler, Inhalt 1 oder 2

codewandlung_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 Windwos- 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     iArgszaehler
        add     pArgs,4
        cmp     iArgszaehler,0
        jne     codewandlung_parameter  ;zur Codewandlung des nächsten Parameters

        mov     eax,pArgsdup    ;pArgs wieder herstellen
        mov     pArgs,eax

;Codewandlung ist beendet. Der erste Parameter (der Text) wird angezeigt
;       mov     eax,pArgs       ;Startadresse des Arrays nach EAX
        mov     ebx,[eax + 4]   ;Startadresse des 1. Aufrufparameters ermitteln
        print   ebx             ;entspricht print offset argument 

;Falls nur ein Aufrufparameter angegeben war, nun Ende mit Errorlevel 0
        cmp     iArgs,2                 
        jne     antw5           ;Sprung, wenn key-list angegeben war
        print   offset crlf     ;Ein Zeilenvorschub 
        mov     eax,0           ;Es war nur ein Parameter!
        ret

;Vergleich der Eingabe gegen die key-list. Dazu wird ebx auf den Anfang
;der key-list gestellt.

antw5:  
        mov     eax,pArgs       ;Startadresse des Arrays nach EAX
        mov     ebx,[eax + 8]   ;Startadresse des 2. Aufrufparameters ermitteln

        write   ">"                     ;> als Prompt. Das print-macro 
;       invoke StdOut,offset prompt     ;verträgt print ">" nicht. deshalb write
;                                       ;oder direktes invoke StdOut        


        call    ret_key         ;wait for a keystroke and return its scancode in EAX
;       getkey                  ;call ret_key als Makro

;       mov     prompt,al       ;kleine Testhilfe
;       print offset prompt
;       jmp antw5

        mov     ecx,1           ;Errorlevel 1 fuer 1. Zeichen der key-list
antw6:  cmp     al,cr
        je      antw5           ;cr wird ignoriert
        
;       and     al,5fh          ;al enth. das Zeichen in Grossschrift

        mov     ah,[ebx]        ;Ende des Vergleichsstring erreicht?
        cmp     ah,0
        je      antw5           ;ja, aber Eingabe war unpassend
        
        cmp     [ebx],al        ;passt die Eingabe?
        je      antw9           ;ja

        inc     ebx             ;nein. Dann Einstellen auf nächsten Vergleich
        inc     ecx        
        jmp     antw6

antw9:  push    ecx             ;ecx enthaelt nun den Errorlevel
        print   offset crlf     ;Zeilenvorschub
        pop     eax             ;eax enthaelt nun den Errorlevel
        ret
    
; 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 Zeichen und 
        mov     al,[ebx]        ;Unterprogrammende        
        ret
      
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

end start                       ; Tell MASM where the program ends

Interpretation des Quellprogramms
Die Interpretation beschränkt sich auf Unterschiede gegenüber dem Testprogramm zum Einlesen der Programmaufrufparameter auf der vorhergehenden Webseite.

Konstanten: Hinzugekommen sind die Symbolisierungen für die deutschen Umlaute im DOS-ASCII. Ihre Verwendung verbessert die Lesbarkeit des Quellprogramms.

Hilfe: Der zur Anzeige bestimmte Text wurde angepasst.

Bei start unter .code wird nun vor der Anforderung zur Tastenbetätigung beim Programmende der Inhalt von eax gesichert und nach der Eingabe wieder hervorgeholt. Grund: Vom Unterprogramm main wird der Errorlevel dem Hauptprogramm in eax übergeben.

.if ... .endif hinter main: Diese Direktiven ermöglichen eine C-ähnliche Syntax bei Bedingungsabfragen. In diesem Fall wird der Assemblercode zwischen .if und .endif nur dann ausgeführt, wenn eax weder den Inhalt 2 oder 3 hat. 2 oder 3 steht für einen oder zwei Kommandozeilenparameter (vergleiche Fehlerhaftes bei cParamFehler). Der eingeschlossene Code bewirkt die Ausgabe der Fehlermeldung und eine vorzeitige Programmbeendigung mit Errorlevel 99.

Mit der in iArgszaehler eingestellten Anzahl der Aufrufparameter wird bewirkt, dass nur der bzw. die vorhandenen Aufrufparameter eine Codewandlung erfahren. Die Programmzeile print ebx und die folgende Programmzeile sind auskommentiert. Die Zeilen blieben erhalten, weil sie als Testhilfen von Nutzen sein können.

;Codewandlung ist beendet: Die beiden ausführbaren Programmzeilen hinter diesem Kommentar zeigen den Prompt auf dem Bildschirm an. Die auskommentierte Programmzeile mov eax,pArgs erinnert daran, dass eax bereits den passenden Inhalt hat.

Falls nur ein Aufrufparameter angegeben war, wird ein Zeilenvorschub ausgegeben und das Unterprogramm wird mit Errorlevel 0 beendet.

antw5 bis zum ret hinter antw6: Diese Zeilen sind an 32-bit-Programmierung angepasste Zeilen aus dem oben genannten 16-bit-Programm. Statt print ">" steht write ">". Das print-Makro kommt mit dem > nicht klar. Alternativ kann der nachfolgende auskommentierte invoke-Aufruf anstelle von write verwendet werden.

Das Makro getkey hat nur wenig Inhalt. Seine vollständige Auflösung steht in der Zeile über dem auskommentierten Makroaufruf.

Der auskommentierte Befehl and al,5fh würde bei seiner Aktivierung eine Wandlung der eingetippten Buchstaben a bis z in Großschrift durchführen. Ob man es macht, ist Entscheidungssache. Wenn man es ordentlich machen möchte, müsste man sich auch um äöü kümmern.


Eine individuelle Überschrift für das Konsolfenster
Wenn man die Zeile beim Label start in
start:  SetConsoleCaption       "Frage ergibt Returncode"
ändert, dann erhält das Konsolfenster die angegebene Überschrift:
Bild: MASM32, individuelle Überschrift für das Konsolfenster
Neben weiteren MAKROS ist SetConsoleCaption in der Hilfestellung des MASM32 SDK beschrieben. Hier ist der Weg zu Beschreibung von der MASM32 SDK Entwicklungsumgebung aus:
  • Reiter „Help”
  • MASM32 Macro High Level Reference
  • Console Mode Macros unter „Macro Categories”
  • SetConsoleCaption

Das Frageprogramm mit PureBasic
Für einen weiteren Vergleich habe ich ein funktionsgleiches Programm in der Programmiersprache Purebasic erstellt. Das daraus ertellte ausführbare 32-bit-Programm Frage.exe kommt auf 12800 Bytes Größe. Dessen Quellprogramm ist allerdings kürzer und deutlich übersichtlicher als das in Assembler geschriebene Quellprogramm:
OpenConsole("Frage ergibt Returncode")

;Zähle Parameter
AnzahlParameter.i = CountProgramParameters()
;Add all command line parameters to a linked list
Global NewList Parameter.s()
;PrintN("Anzahl Aufrufparameter: " + StrU(AnzahlParameter))

Select AnzahlParameter
  Case 1
    AddElement(Parameter()) : a$=ProgramParameter() : PrintN (a$)
    ExitCode=0      ;Errorlevel 0 wenn Aufruf mit nur einem Parameter
 
  Case 2
    AddElement(Parameter()) : prompt$=ProgramParameter() : Print(prompt$ + "> ")
    AddElement(Parameter()) : antworten$=ProgramParameter()
    ;PrintN ("ExitCode=" + StrU(ExitCode)) 
  
    Repeat      ;Vergleich der Eingabe ohne Beachtung der Groß-/Kleinschreibung
      KeyPressed$ = Inkey()
        If KeyPressed$ <> ""
          Position = FindString(antworten$, KeyPressed$,1,#PB_String_NoCase)
          Debug position
        Else
          Delay(100) ; Wir verbrauchen nicht die gesamte
                     ; CPU-Leistung, da wir in einem Multitasking-OS
		     ; laufen
        EndIf
      
        If KeyPressed$ = Chr(27)  ;Abbruch mit ESC-Taste wird ermoeglicht
          Position=99
        EndIf  
      
    Until Position<>0      ;Bis gültige Eingabe oder ESC-Taste
    ExitCode=Position      ;Errorlevel 99 kennzeichnet Fehler oder Abbruch

  Default      
    PrintN ("Aufruf:   FRAGE.exe prompt [key-List]")
    PrintN ("")
    PrintN ("Beispiel: frage.exe " + Chr(34) + "Weitermachen (jn)" + Chr(34) + " jn")
    PrintN ("Ohne key-List kann mit einer beliebigen Taste geantwortet werden")
    PrintN ("Prompt ggf. in Gänsefüßchen!")
    ExitCode=99      ;Errorlevel 99 kennzeichnet Fehler
EndSelect
 
;PrintN ("ExitCode=" + StrU(ExitCode))
PrintN("")
PrintN("Press any key To Continue ...")
Input()     
End ExitCode

Als Reste von der Programmentwicklung enthält das Quellprogramm an zwei Stellen auskommentierte Anzeigebefehle für den erzeugten Rückgabecode:
;PrintN ("ExitCode=" + StrU(ExitCode))

Weiterhin ist der Befehl

Debug position
vorhanden. Dieser ruft eine eingebaute Testhilfe auf. Bei der Compilierung in das ausführbaren Program Frage.exe wird dieser Befehl wie ein Kommentar behandelt. Somit verlängert er das ausführbare Programm nicht.

Laut der Produktbeschreibung für PureBasic soll der erzeugte ausführbare Programmcode verhältnismäßig kompakt sein. In diesem Fall ist er jedoch gut viermal umfangreicher als sein mit purem Assembler erzeugtes Gegenstück.


Letztes Upload: 17.10.2016 um 18:11:44