Frage deutsch
~~~~~~~~~~~~~~
Wie teile ich mein Programm mit CHAIN auf 2 Dateien auf?
Question English
~~~~~~~~~~~~~~
How to split my program into 2 files using CHAIN?
Antwort 1
~~~~~~~~~~
[ von Thomas Antoni, 11.8.2003 - 26.1.2006 ]
.
*** Inhalt
1. Wozu ist CHAIN gut - das Wichtigste in Kürze
2. Ein einfaches Programmbeispiel "CHAIN1"
3. Alles über CHAIN
- Gemeinsam benutzte Variable, Felder und externe Dateien
- Strategien zur Programmaufteilung
- CHAIN bei kompilierten EXE-Programmen
4. Ein ausführliches Programmbeispiel "CHAIN2"
5. Weitere Möglichkeiten der modularen Programmierung
- Übersicht
- Externes Q(uick)Basic-BAS- und -EXE Programme starten mit RUN
- Beliebige EXE-Programme starten mit SHELL
- Module bei QuickBasic (MAK-Moule)
- Bibliotheken bei QuickBASIC
- INCLUDE-Dateien bei QuickBASIC
*** 1. Wozu ist CHAIN gut - das Wichtigste in Kürze
CHAIN ist interessant für fortgeschrittene QBasic-Programmierer, deren
Programm irgendwann die maximale Größe von 116 KB überschreitet und daher nicht
mehr in den Speicher passt. QBasic-Einsteiger brauchen sich einstweilen mit
CHAIN nicht zu befassen.
Der CHAIN-Befehl ermöglicht es, ein Programm in zwei oder mehr
Programmdateien aufzuteilen und zwischen diesen hin- und herzuspringen, d.h.
diese zu "verketten" (chain = engl. "Kette", "verketten"). CHAIN hat die
Syntax
CHAIN "Laufwerk:\Pfad\prog2.bas"
Dieser Befehl bewirkt, dass sich das momentan gerade laufende BAS-Programm
aus dem Hauptspeicher entfernt und die Kontrolle an prog2.BAS übergibt. Die
Programm-Abarbeitung wird am Anfang von prog2.BAS fortgesetzt. Eventuell noch
offene externe Dateien bleiben geöffnet. In vielen Fällen wird prog2.BAS
irgendwann wiederum per CHAIN zum aufrufenden Programm zurückverzweigen.
Die per CHAIN verketteten Programmdateien nennt man in der IT-Fachsprache
auch "Module" und die nachgeladenen Module "Overlays", da sie das aufrufende
Programm aus dem Hauptspeicher verdrängen (diesem überlagert werden; engl.
overlay = "überlagern", Überlagerung").
Variablen und Felder können von den Programmodulen gemeinsam benutzt werden,
wenn Du sie in jedem Modul mit COMMON als "globale" Daten deklarierst.
Auch EXE-Programme können geCHAINt werden. Dazu später mehr im Abschnitt
"Alles über CHAIN".
Bei QBasic stehen für die modulare Programmierung (auf mehrere Dateien
aufgeteilte Programme nur die Befehle CHAIN und RUN zur Verfügung. Bei
QuickBasic gibt es wesentlich fortgeschrittenere und leistungsfähigere Methoden,
die im Kapitel 5. "Andere Möglichkeiten der modularen Programmierung"
aufgelistet sind.
*** 2. Ein einfaches Programmbeispiel: "CHAIN1"
Bevor wir uns mit den theoretischen Details von CHAIN befassen, nun erstmal
ein ganz einfaches Programmbeispiel.
Die beiden unten aufgeführten Programmdateien CHAIN1-1.BAS und CHAIN1-2.BAS
demonstrieren, wie Du ein zu groß gewordenes QBasic-Programm mit Hilfe des
CHAIN-Befehls in zwei Teilprogramme aufspalten und verketten kannst.
CHAIN1-1.BAS ist das "Hauptmodul", das zuerst gestartet wird. Es erfragt die
Zahl A und ruft dann das Untermodul CHAIN1-2.BAS auf, welches das Quadrat A^2
von A ermittelt und anzeigt. CHAIN1-2.BAS beendet sich nach der Betätigung einer
beliebigen Taste. Die gemeinsam benutzte Variable A muss in beiden Modulen mit
COMMON deklariert werden.
Die beiden Programmdateien CHAIN1-1.BAS und CHAIN1-2.BAS müssen ins
Verzeichnis C:\CHAIN1\ kopiert werden. Alle 2 Programme steht im
ZIP-Archiv Progs\Chain1.ZIP zur Verfügung sowie online unter www.antonis.de/faq/progs/chain1.zip
.
Am Besten, Du entpackst Chain1.zip nach C:\ .
'*******************************************************************
' CHAIN1-1.BAS = Demoprogramm fuer die Programmverkettung per CHAIN
' ============
' Nach der Eingabe einer Zahl A wird das Programmmodul
' CHAIN1-2.BAS aufgerufen, welches das Quadrat A^2 von
' A bildet und anzeigt.
'
' CAIN1-1.BAS und CHAIN1-2.BAS muessen sich beide im Verzeichnis
' C:\CHAIN1\ befinden.
'
' (c) Thomas Antoni, 11.8.2003 - 22.8.2003
'*******************************************************************
COMMON A AS SINGLE
'Variable A! vom Typ SINGLE deklarieren (einfach lange
'Gleitunktzahl). COMMON bewirkt, dass A! in allen
'geCHAINten Modulen bekannt ist
CLS
INPUT "Gib eine Zahl ein: A = "; A!
CHAIN "c:\chain1\chain1-2.bas"
'*****************************************************************
' CHAIN1-2.BAS = Demoprogramm fuer die Programmverkettung per CHAIN
' ============
' Diese Programmdatei wird von CHAIN1-1.BAS per CHAIN aufgerufen.
' Es bildet das Quadrat A^2 der Eingabezahl A und beendet sich
' dann.
'
' (c) Thomas Antoni, 11.8.2003 - 11.8.2003
'*****************************************************************
COMMON A AS SINGLE
PRINT " A ^ 2 = "; A! ^ 2
PRINT "Beenden mit beliebiger Taste"
SLEEP
END
*** 3. Alles über CHAIN
Gemeinsam benutzte Variable, Felder und externe Dateien
Alle externen geöfneten Dateien bleiben beim Aufruf eines anderen
Programmmoduls mit CHAIN weiterhin geöffnet. Das gechainte Programm muss sie
also nicht erneut öffnen.
Die Variablen und Felder, die in allen Modulen zugreifbar sein sollen, müssen
am Anfang einer jeden Moduldatei mit COMMON deklariert werden.
Für Felder muss anschließend mit einem DIM oder REDIM-Befehl die
Dimensionierung (Größenangabe) erfolgen. Diese Dimensionierung ist nur bei dem
als erstes gestarteten "Hauptmodul" sinnvoll und erforderlich. Bei den
"Untermodulen" muss sie entfallen. Bei mehrfach durchlaufenen Programm-Modulen
ist der REDIM-Befehls statt des DIM-Befehls zu verwenden, weil sonst ab dem
zweiten Durchlauf bei jedem Aufruf des Hauptmoduls eine Fehlermeldung wegen
doppelter Dimensionierung erscheint. QBasic legt globale, mit COMMON deklarierte
Felder normalerweise als dynamische Felder an.
Beispiel für COMMON-Deklaration im Hauptmodul:
COMMON A(), B AS SINGLE, berechnet AS INTEGER
REDIM A!(2)
Beispiel für COMMON-Deklaration im Untermodul (ohne
DIM/REDIM):
COMMON A(), B AS SINGLE, berechnet AS INTEGER
Beim Hauptmodul ist hier REDIM statt DIM verwendet, weil wir annehmen, dass
das Hauptmodul mehrfach durchlaufen wird.
Variablen und Felder, die auch in allen SUBs und FUNCTIONs eines
Programmmoduls zugreifbar sein sollen, werden mit einem SHARED-Attribut hinter
dem COMMON entsprechend gekennzeichnet
Beispiel für COMMON SHARED:
COMMON SHARED A(), B AS SINGLE, berechnet AS INTEGER
Das SHARED bewirkt, dass A!(), B! und berechnet% nicht nur in allen
Moduldateien, sondern auch in allen SUB/FUNCTINs bekannt sind.
Die Reihenfolge der Variablen und Felder in den COMON-Deklarationen muss in
allen geschainten Modulen unbedingt gleich sein, weil die Module darauf nicht
über die Variablennamen, sondern über absolute Speicheradressen
zugreifen.
Das untenstehende Programmbeispiel CHAIN2 verdeutlicht die Verwendung des
COMMON
Befehls an einem praktischen Beispiel.
Strategien zur Programmaufteilung
Willst Du ein fertiges Programm nachträglich teilen und per CHAIN verketten,
musst Du es in der Regel in seiner Struktur etwas anpassen. Dabei ist zu
bedenken, dass jedes gechainte Modul ab Programmbeginn durchlaufen wird. Ein
Sprung mitten in Programm hinein ist mit CHAIN leider nicht möglich. Auch der
Aufruf von SUB/FUNCTIONs, die sich in anderen Modulen befinden, wird nicht
unterstützt - häufig wird man also nicht darum herumkommen, eine bestimmte
SUB/FUNCTION in mehrere Module einzufügen.
Am besten, Du organisierst Dein Hauptmodul von Beginn bis Ende als
Dauerschleife, in der abhängig von den Anwendereingaben die Untermodule per
CHAIN aufgerufen werden. Wenn vor dem ersten Schleifendurchlauf bestimmte
einmalige Aktionen ("Initialisierungen"" erforderlich sind, dann kannst Du die
Abarbeitung der entsprechenden Programmsequenz vom Zustand "0" eines
"Erstlaufmerkers" abhängig machen, der von den Untermoulen auf "1" gesetzt wird.
Über "IF erstlaufmerker = 0 THEN..." erkennt das Hauptmodul, dass es zum
allerersten Mal durchlaufen wird und die Initialisierung durchführen muss.
Ein Beispiel für dieses Vorgehen findest Du im untenstehenden
Programmbeispiel CHAIN2. Dort heißt die Erstlaufkennung "berechnet%".
QBasic unterstützt die Aufteilung von Programmen nur sehr eingeschränkt über
die Befehle CHAIN und RUN. QuickBasic 4.x und 7.x bieten da wesentlich mehr
Möglichkeiten, die im untenstehenden Kapitel 5 "Andere Möglichkeiten der
modularen Programmierung" kurz erläutert sind.
CHAIN bei kompilierten EXE-Programmen
Auch EXE-Programme kann man problemlos chainen. Die BAS-Quellsprachedateien
müssen dafür bei QuickBasic 4.5 unbedingt mit der Option "EXE, die BRUN45.EXE
benötigt" zu EXE-Dateien kompiliert werden, sonst lassen sich keine COMMON-Daten
gemeinsam benutzen. Das Laufzeitmodul BRUN45.EXE muss sich in demselben
Verzeichnis wie die ausführbaren Dateien befinden. Beim Weitergeben Deiner
Programmdateien an dritte Personen muss Du also darauf achten, dass Du
BRUN45.EXE mit weitergibst. Das Laufzeitmodul bleibt immer im Hauptspeicher
enthalten und muss nicht mit jedem neuen CHAIN-Befehl neu geladen werden.
Wenn Du die geCHAINten Programme nicht innerhalb der QB- Enwicklungsumgebung,
sondern über die Kommandozeile kompilierst, musst Du die Option /O beim Aufruf
von BC.EXE weglassen, um EXE-Programme mit externem BRUN45.EXE Laufzeitmodul zu
erzeugen.
Den Namen der Zieldatei musst Du im CHAIN-Befehl jeweils von .BAS in .EXE
ändern. Ansonsten können die BAS-Dateien gegenüber der interpretierten Version
unverändert bleiben.
Bei Verwendung von QuickBasic solltest Du die COMMON-Deklarationen in einer
Include Datei zusammenfasen, die Du in allen Modulen per $INCLUDE einfügst (Der
IT-Experte redet auch von "includieren"). Dadurch ist sicher gewährleistet, dass
der COMMON-Block und damit die Reihenfolge der Variablen und Felder in allen
Modulen wirklich exakt gleich ist. Damit entfällt eine häufige Fehlerquelle.
Beispielprogramme für das Chainen von EXE-Programmen:
Die ZIP-Archive Chain1.zip und Chain2.zip enthalten Beispiele für das Chainen
von EXE-Dateien. Im jeweiligen Unterverzeichnis /Exe/ der ZIP-Archive findest Du
sowohl das oben erläuterte einfache Beispiel mit CHAIN1-1.BAS und CHAIN1-2.bas
als auch das weiter unten aufgeführte ausführliche Beispiel mit CHAIN2-1.BAS,
CHAIN2-2.BAS und CHAIN2-2.BAS . Die
beiden ZIP-Archive findest Du unter Progs\Chain1.ZIP bzw. Progs\Chain1.ZIP sowie
online unter www.antonis.de/faq/progs/Chain1.zip
bzw. www.antonis.de/faq/progs/Chain2.zip .
Die ZIP-Archive müssen nach c:\Chain1\ bzw c:\chain1\ entpackt werden.
*** 4. Ein ausführliches Programmbeispiel: "CHAIN2"
Die drei untenstehenden Programme CHAIN2-1, CHAIN2-3.BAS und CHAIN2-3.BAS
demonstrieren, wie Du ein zu groß gewordenes QBasic-Programm mit Hilfe des
CHAIN-Befehls in ein Hauptmodul und 2 Untermodule aufspalten und verketten
kannst. Die sich im Wechsel gegenseitig aufrufen.
Hierbei wird gezeigt, wie nicht nur auf Variable, sondern auch auf Felder ein
gemeinsamer Zugriff durch alle Module möglich ist.
Das Hauptmodul CHAIN2-1.BAS erfragt zwei Zahlen, legt sie in die Feldelemente
A!(0) und A!(1) ab und verzweigt abhaengig davon, ob Multiplizieren oder
Dividieren angefordert ist, per CHAIN-Befehl zum Programmmodul CHAIN2-2.BAS oder
CHAIN2-3.BAS. Hier wird die gewünschte Rechenfunktion durchgeführt, das
Rechenergebnis in der globalen Variablen B! hinterlegt und anschließend zum
Hauptmodul CHAIN2-1.BAS zurückverzweigt. Dieses zeigt das Rechenergebnis an.
Beim ersten Programmdurchlauf darf jedoch keine Ergebnisanzeige erfolgen, da ja
noch kein Ergebnis vorliegt. Dies erkennt das Hauptmodul am Vorbesetzungswert
"0" der globalen Variablen berechnet% . Die Untermodule setzen berechnet% nach
jeder Berechnung auf "1".
Die drei Programmdateien CHAIN2-1.BAS, CHAIN2-2.BAS und CHAIN3-2.BAS müssen
ins Verzeichnis C:\CHAIN2\ kopiert werden. Alle 3 Programme steht im ZIP-Archiv Progs\Chain2.ZIP zur Verfügung sowie
online unter www.antonis.de/faq/progs/chain2.zip .
Am Besten, Du entpackst Chain2.zip nach C:\ .
Und hier nun die drei Beispielprogramme:
'*******************************************************************
' CHAIN2-1.BAS = Demoprogramm fuer die Programmverkettung per CHAIN
' ============
' Nach der Eingabe der zwei Zahlen A!(0) und A!(1) wird abhaengig
' davon, ob Multiplizieren oder Dividieren angefordert ist,
' das Programmmodul CHAIN2-2.BAS oder CHAIN2-3.BAS aufgerufen.
'
' Das aufgerufene Modul fuehrt die gewuenschte Rechenoperation
' durch, hinterlegt das Ergebnis in B! und verzweigt per CHAIN
' zutueck zu CHAIN2-1.BAS.
'
' CAIN2-1.BAS, CHAIN2-2.BAS und CHAIN1-3.BAS muessen sich im
' Verzeichnis C:\CHAIN2\ befinden.
'
' (c) Thomas Antoni, 12.8.2003 - 13.8.2003
'*******************************************************************
COMMON A(), B AS SINGLE, berechnet AS INTEGER
'Feld A!(2) und die Variablen B! und n´berechnet% deklarieren.
'COMMON bewirkt, dass diese Variablen in allen geCHAINten Modulen
'global bekannt und zugreifbar sind
REDIM A!(2)
'globale Felder muessen erst mit COMMON deklariert und dann
'mit REDIM oder DIM dimensioniert werden. Hier ist REDIM
'verwendet, weil sonst ab dem zweiten Durchlauf bei jedem
'Aufruf des Hauptmoduls eine Fehlermeldung wegen doppelter
'Dimensionierung erscheint.
'Globale Felder sind grundsaetzlich dynamisch.
DO
IF berechnet% = 1 THEN 'Ergebnis nur anzeigen wenn eins vorliegt
PRINT
PRINT "Ergebnis: B = "; B!
PRINT
PRINT "Neue Berechnung....[beliebige Taste] Beenden....[Esc] "
DO: taste$ = INKEY$: LOOP WHILE taste$ = ""
'warten auf Tastenbetaetigung
IF taste$ = CHR$(27) THEN END 'Beenden mit Esc-Taste
END IF
'
CLS
INPUT "Gib die erste Zahl ein A(0) = "; A!(0)
INPUT "Gib die zweite Zahl ein A(1) = "; A!(1)
PRINT
INPUT "Was willst Du tun? 1=Multiplizieren..2=Dividieren "; tun%
SELECT CASE tun%
CASE 1: CHAIN "c:\chain2\chain2-2.bas" 'Multiplizieren
CASE 2: CHAIN "c:\chain2\chain2-3.bas" 'Dividieren
END SELECT
LOOP
'*****************************************************************
' CHAIN2-2.BAS = Demoprogramm fuer die Programmverkettung per CHAIN
' ============
' Diese Programmdatei wird von CHAIN2-1.BAS per CHAIN aufgerufen.
' Es multipliziert A!(0) mit A!(1) und hinterlegt das Ergebnis
' in B!. Anschliessend erfolgt per CHAIN ein Ruecksprung ins
' Hauptmodul.
'
' (c) Thomas Antoni, 12.8.2003 - 13.8.2003
'*****************************************************************
COMMON A(), B AS SINGLE, berechnet AS INTEGER
'Deklaration der gemeinsamen ("globalen") Variablen.
'Die Reihenfolge der Variablen muss in allen Modulen
'unbedingt gleich sein!
'Eine Dimensionierung des Feldes mit REDIM A(2) ist nur im
'Hauptmodul CHAIN2-1.BAS sinnvoll und erforderlich.
B! = A!(0) * A!(1)
berechnet% = 1 'Nachricht ans Hauptmodul, dass ein
' Berechnungsergebnis vorliegt
CHAIN "c:\chain2\chain2-1.bas" 'Ruecksprung zum Hauptmodul
END
'*****************************************************************
' CHAIN2-3.BAS = Demoprogramm fuer die Programmverkettung per CHAIN
' ============
' Diese Programmdatei wird von CHAIN2-1.BAS per CHAIN aufgerufen.
' Es fuehrt die Division A!(0) / A!(1) durch und hinterlegt das
' Ergebnis in B!. Anschliessend erfolgt per CHAIN ein Ruecksprung
' ins Hauptmodul.
'
' (c) Thomas Antoni, 12.8.2003 - 13.8.2003
'*****************************************************************
COMMON A(), B AS SINGLE, berechnet AS INTEGER
'Deklaration der gemeinsamen ("globalen") Variablen.
B! = A!(0) / A!(1)
berechnet% = 1 'Nachricht ans Hauptmodul, dass ein
' Berechnungsergebnis vorliegt
CHAIN "c:\chain2\chain2-1.bas" 'Ruecksprung zum Hauptmodul
END
Ein sehr interessantes Anwendung von CHAIN findest Du in meinem Formel-Löser
FORMEL.BAS. Der ist enthalten im FAQ-Eintrag "Zahlen verarbeiten, mathematische Probleme -> Wie
berechne ich das Ergebnis eines eingegebenen Formelausdrucks?" .
*** 5. Weitere Möglichkeiten der modularen Programmierung
Übersicht
Unter modularer Programmierung versteht man das Aufteilen eines Programms in
mehrere, möglichst in sich abgeschlossene Programmdateien. Im Idealfall können
diese getrennt getestet und vielfältig für andere Programme wiederverwendet
werden. Die Daten- und Aufrufschnittstellen zwischen den einzelnen Modulen
sollen möglichst schlank sein, um Fehlerquellen auszuschließen.
In QBasic und QuickBasic gibt es vielfältige Möglichkeiten der modularen
Programmierung, die den Einsteiger häufig verwirren und auch in der Online-Hilfe
und in der Literatur oft nur unvollständig beschrieben sind.
Im Wesentlichen gibt es bei QuickBASIC außer dem oben im Detail erläuterten
CHAIN die folgenden, in den nächsten Abschnitten näher beschriebenen Varianten
der modularen Programmierung:
• Externes Q(uick)Basic-BAS- oder -EXE
Programme starten mit RUN (siehe den FAQ-Eintrag "Fragen zu speziellen QBasic-Befehlen -> Was ist der
Unterschied zwischen CHAIN und RUN?"
)
• Beliebige EXE-Programme starten mit
SHELL
• Verwendung von MAK-Modulen (nicht
bei QBasic, nur bei QuickBasic) siehe den FAQ-Eintrag "Bibliotheken, CHAIN- und MAK-Module -> Was sind
MAK-Module und wie gehe ich mit ihnen um?"
.
• Verwendung von Bibliotheken (nicht bei
QBasic, nur bei QuickBasic; siehe "QuickBASIC und VBDOS -> Was ist eine Bibliothek und wie gehe ich
damit um?"
• INCLUDE-Dateien (bei QuickBASIC)
Externes Q(uick)Basic-BAS- und -EXE Programme starten mit RUN
RUN funktioniert fast genauso wie CHAIN:. Es entfernt das aktuelle Programm
aus dem Hauptspeicher und startet das angegebene Q(uick)Basic-Programm.
Die Syntax lautet
RUN [Zeilennummer]
["Programmname"]
"Programmname" ist der Name einer BASIC Quelldatei oder einer mit QuickBasic
kompilierten EXE-Datei. Wenn die Namenserweiterung fehlt, nimmt QBASIC an, daß
der Dateiname die Erweiterung .BAS besitzt. Fehlt der "Programmname", so wird
zum aktuell gerade ablaufenden Programm gesprungen (Sprung auf sich selbst).
Wie oben schon gesagt, kann man auch eine EXE Datei per RUN starten, auch
wenn dies in der QuickBasic Online-Hilfe nicht besonders erwähnt wird.
Die optionale Zeilennummer gibt an, wo mit der Ausführung des aktuellen
Programms begonnen werden soll. Wenn keine Zeilennummer angegeben wird, beginnt
die Ausführung in der ersten ausführbaren Zeile. Die Zeilennummer kann leider
nur angegeben werden, wenn der "Programmname" fehlt, also wenn sich das aktuell
laufende Programm sich per RUN selber neulädt. Damit ist diese Option relativ
nutzlos.
Die optionale Zeilennummer gibt an, wo die Ausführung des aktuellen Programms
begonnen werden soll. Wenn keine Zeilennummer angegeben wird, beginnt die
Ausführung in der ersten ausführbaren Zeile. Die Zeilennummer kann leider nur
angegeben werden, wenn der "Programmname" fehlt, also wenn kein externes,
sondern das aktuell gerade ablaufende Programm neu gestartet wird.
Unterschiede zwischen RUN und CHAIN:
- RUN schließt im Gegensatz zu CHAIN alle Dateien und löscht den
Variablenspeicher vor dem Laden eines Programms. Die Benutzung gemeinsamer, mit
COMMON deklarierter Variablen und Felder ist nicht möglich.
- Mit RUN kann ein Programm sich selbst aufrufen. Das ist mit CHAIN nicht
möglich. Dabei kann man an sogar an eine beliebige Stelle des gerade laufenden
(aufrufenden) Programms springen. Hierzu muss diese Stelle mit einer Zeilenummer
versehen sein. Dies ist nur beim aktuellen, leider nicht bei externen Programmen
möglich. Mit CHAIN kann man immer nur an den Anfang des verketteten Programms
springen. Mir fällt aber keine Anwendung dieses "Programm-selbst-Neustartens"
ein; höchstens, dass mann auf diesem Wege alle Felder und Variablen mit einem
Sclag neu initialisieren (nullsetzen) kann.
- RUN kann wie CHAIN auch auf kompilierte EXE-Programme angewendet
werden. Das funktioniert im Gegensatz zu CHAIN auch bei eigenständigen
EXE-Dateien und nicht nur bei solchen, die das Runtime-Modul BRUN45.EXE zum
Ablaufen benötigen.
Meiner Meinung nach ist RUN ein Überbleibsel aus älteren BASIC-Dialekten wie
QW-BASIC. CHAIN bietet wesentlich mehr Möglichkeiten und ist daher eher zu
empfehlen.
Beispiel für die Programmverkettung über den RUN-Befehl
Die beiden folgenden Progrämmchen müssen sich im Verzeichnis C:\RUN\
befinden:
'RUN1-1.BAS
PRINT "Demo des RUN-Befehls"
RUN "c:\run\run1-2.bas"
END
'RUN1-2.BAS
PRINT "Beenden mit beliebiger Taste"
SLEEP
END
Beim Start des ersten Programms 'RUN1-1.BAS ergibt sich die folgende
Bildschirmanzeige:
Demo des RUN-Befehls
Beenden mit beliebiger Taste
Beispiel für Neustart des aktuellen Programms mit RUN
Hier ein etwas konstruiertes Beispiel eines Programms, das sich mit RUN
selbst aufruft, wenn der Anwender fehlerhafterweise einen Leerstring statt
seines Namens eingibt (Eingabetaste ohne vorher einen Buchstaben einzugeben)
:
CLS
INPUT "Gib Deinen Namen ein: ", Name$
IF Name$ = "" THEN RUN
'Programm neustarten wenn kein Name eingegeben, sondern nur Eingabetaste
PRINT "Hallo "; Name$
SLEEP
END
Beliebige EXE-Programme starten mit SHELL
SHELL ermöglicht den Aufruf beliebiger DOS- und Windows-Kommadozeilenbefehle.
Gibt man als Parameter eine ausführbare EXE-Datei an, so lassen sich damit auch
externe Programme starten.
SHELL arbeitet ähnlich wie RUN mit den folgenden Unterschieden:
- Es sind nur EXE-, keine BAS-Programme aufrufbar.
- Die EXE-Programme können nicht nur mit QuickBasic, sondern mit
beliebigen
Programmiersprachen erstellt werden.
- Das aufrufende Programm entfernt sich nicht selber aus dem
Hauptspeicher,
sondern bleibt in "Wartestellung" dort vorhanden.
Beispiel für Programmaufruf per SHELL
Die beiden folgenden Progrämmchen müssen zu EXE-Dateien kompiliert und ins
Verzeichnis C:\SHELL\ kopiert werden:
'SHELL1-1.BAS
PRINT "Demo des SHELL-Befehls"
SHELL "c:\shell\SHELL1-2.EXE"
END
'SHELL1-2.BAS
PRINT "Beenden mit beliebiger Taste"
SLEEP
END
Beim Start des ersten Programms SHELL1-1.EXE ergibt sich die folgende
Bildschirmanzeige:
Demo des SHELL-Befehls
Beenden mit beliebiger Taste
Die Programmverkettung per SHELL ist für QB-Programmierer nur interessant,
wenn Programme aufgerufen werden sollen, die in einer anderen Programmiersprache
geschrieben wurden. Für die Verkettung von QB-Programmen bietet CHAIN wesentlich
mehr Möglichkeiten.
Module bei QuickBasic (MAK-Moule)
Unter QuickBasic 4.5 und 7.1 kannst Du Deine Programme sehr elegant in
mehrere Module aufteilen, um die Speicherbeschränkung eines EXE-Programms auf 64
KB zu umgehen. In QBasic steht dagegen nur die umständliche Verkettungsmethode
mit CHAIN und RUN zur Verfügung.
Diese Module will ich hier mal salopp "MAK-Module" nennen, weil QuickBasic
die zu einem Programm gehörenden Quellsprache-Module in einer MAK-Datei
auflistet.
MAK- Module haben folgende Eigenschaften:
- Für jeden Modul gibt es eine eigene Quellsprachedatei.
- Das Maschinenprogramm kann je Modul bis zu 64 KB groß sein, eine aus
mehreren Modulen erstellte EXE-Datei weit über 200 KB.
- Die Module werden innerhalb der Entwicklungsumgebung zusammengebunden (über
"Datei | Laden" und "Ausführen | Hauptmodul bestimmen") oder auf
Maschinensprache-Ebene über den Linker.
- Es gibt normalerweise ein Hauptmodul, das beim Aufruf des Gesamtprogramms
gestartet wird und ein oder mehrere Untermodule, die nur SUBs und FUNCTIONs
enthalten. Die SUBs und FUNCTIONs eines Moduls sind auch von allen anderen
Modulen aus zugreifbar.
- Variable und Felder, die in allen Modulen mit COMMON deklariert sind,
lassen sich Modul-übergreifend ansprechen.Mit COMMON SHARED deklarierte Daten
sind auch in allen SUB/FUNCTIONs zugreifbar.
Mehr Informationen zu MAK-Modulen erhältst Du im FAQ-Eintrag "QuickBASIC und VBDOS -> Was sind MAK-Module
und wie gehe ich mit ihnen um?" .
Bibliotheken bei QuickBASIC
QuickBASIC (nicht QBasic) bietet die Möglichkeit, Bibliotheken zu einem
Hauptprogramm hinzuzubinden. Eine Bibliothek ist nichts anderes als eine Datei
mit einer Sammlung häufig benutzter SUBroutinen und FUNCTIONs, die in
Maschinensprache vorliegen und zum Anwenderprogramm hinzugebunden werden. Durch
die Verwendung von Bibliotheken ist keine Abhilfe bei Speicherüberlauf-Problemen
zu erwarten, weil sie zum Hauptmodul mit hinzugebunden werden und dessen
Speichergrenze von 64 KB nicht erhöhen.
Im Gegensatz zu den MAK-Modulen werden Bibliotheken nicht grundsätzlich
komplett zum Hauptprogramm hinzugebunden, sondern nur die tatsächlich
aufgerufenen SUBs und FUNCTIONs.
Das Thema "Biblotheken" wird ausführlich in den entsprechenden Einträgen der
Kategorie QuickBasic und VB-DOS erläutert.
INCLUDE-Dateien bei QuickBASIC
In QuickBASIC, nicht in QBasic, kann man Quellcode, der in einer anderen
Datei hinterlegt ist mit dem "Metabefehl"
'$INCLUDE: 'Dateiname'
in sein Programm einbinden (nicht den Doppelpunkt vergessen!).
Antwort 2
~~~~~~~~~
[ von Minisoft ("Ray" minisoft*arcor.de ) im QB-Forum, 29.7.2003 ]
Programme mit CHAIN verketten - dieses Beispiel-Programm zeigt, wie das
geht:
'----------------------------
'Visit http://home.arcor.de/minisoft
'e-mail: minisoft*arcor.de
'1. Teil
'----------------------------
cls
Print "Hallo das ist der erste Teil"
Print "Strg drücken
sleep
cls
chain "c:\teil2" 'Pfad in klammer tippen
'teil2 'steht für den namen des anderen teils
IM ANDEREN PROGRAMM DANN VIELLEICHT SO:
cls
Print "(1) Teil1 laden
Print "(2) Beenden
do
a$=inkey$
wahl$=a$
If wahl$="1" then chain "c:\teil1"
If wahl$="2"then end
loop
Answer 3
~~~~~~~~~~~~~~
[ By Thomas Antoni & Robert J (A.V. Paging & Cellular) ]
.
*** How does CHAIN work?
CHAIN offers help for QB-Programmers who's program exceeds the 116 KB memory
limit. You can split up your BAS programm into two or more program files and
jump between them by CHAIN.
CAIN has the following syntax:
CHAIN "drive:\path\prog2.bas"
This statement removes the actual program from memory and transfer control to
prog2.bas. The program execution starts at the first executable statement of
prog2.bas. Opened files remain open. Its not necessary for the new programm to
reopen them. In many cases prog2.bas will jump back again to the original
programm by CHAIN.
Variables and arrays can be jointly accessed by the chained program modules
if they are declared as global data via COMMON statements.
Arrays have to be dimensioned by subsequent DIM statements. This is only
necessary in the main module, not in the submodules. Use REDIM instead of DIM if
the main module ist executed multiple times. Otherwise an error message due to
"Multiple Dimensioning" will occur.
Example for a COMMON-Declaration in the main module:
COMMON A(), B AS SINGLE, C AS INTEGER
REDIM A!(2)
Example for a COMMON-Declaration in a submodule (without
DIM/REDIM):
COMMON A(), B AS SINGLE, C AS INTEGER
REDIM ist used in the main module instead of DIM because the assumption is
made that it is repeatedly excecuted in a CHAIN loop.
Variables and arrays which should also be accessable in the SUBs and
FUNCTIONs by all modules must be declared by COMMON SHARED.
EXAMPLE for COMMON SHARED:
COMMON SHARED A(), B AS SINGLE, berechnet AS INTEGER
The succesion of the variables and arrays in the COMMON statements of all
modules must be exactly the same because they are accessed by their absolute
memory addresses and not by their induvidual names.
CHAIN can also be used for chaining EXE files. This will only work by
compiling the BAS files by using the QuickBasic 4.5 compiler (without the BC.EXE
/o option) resulting to EXE files requiringing the BRUN45.EXE runtime
module.
*** A simple program example for CHAIN
Copy the following two progs into a directory named "C:\CHAIN\":
'--- CHAIN1-1.BAS ---
COMMON A AS SINGLE
CLS
INPUT "Enter a number: A = "; A!
CHAIN "c:\chain1\chain1-2.bas"
'-- CHAIN1-2.BAS ---
COMMON A AS SINGLE
PRINT " A ^ 2 = "; A! ^ 2
PRINT "Terminate with any key"
SLEEP
END
The first program CHAIN1-1.BAS asks the user to enter a number A and
afterwards transfers control to CHAIN1-2.BAS via CHAIN.
The second program CHAIN12.BAS calculates the square value of A, displays it
on the screen and terminates itself.
*** How to CHAIN non .BAS extensions in QB, e.g. EXE progs
?
Here is a demonstration of how to pass control from a main program executable
to a chained executable that has been renamed after compiling and linking. The
renamed extension in this case is (.abc).
The only restriction in this method is that you cannot use the /o compile
option with BC.exe. You must use the applicable QuickBasic or PDS Basic runtime
library.
Compile, link and renaming instructions (make sure bc, link, and runtime
libraries are set on the path or in a LIB= environment variable ...
1. bc main;
2. link main,,,;
3. bc chained;
4. link chained,,,;
5. rename chained.exe chained.abc
The Chain statement can also designate drive and directory path information
... CHAIN "AnyDrive:\AnyPath\Chained.abc". Just remember to create directory and
tuck programs there, blah-blah-blah.
In addition, the chained to program, in this case Chained.abc, could have any
extension. I only used .abc as an example. Of course, .exe would work, but
defeats the stated purpose of this demonstration, and I would probably avoid a
.com extension as well.
Of course, while this technique works, programmers know that for every giveth
there is a taketh away :(. The giveth is that the technique works and the taketh
away is that the the /o compiler option (standalone) cannot be used. You must
rely on an applicable runtime library.
See CHAIN, COMMON and COMMON SHARED in the QB online help for more
information. Hope this helps you with your dilemma.
'****************************************************************************
'** Main.bas ******************************************************
'
'Demonstate how to pass control from a main executable to
'a renamed executable.
'This technique will only work by compiling without the /o option
'resulting to EXE files requiringing the BRUN45.EXE runtime module.
'
DEFINT A-Z
OPTION BASE 1
REM $DYNAMIC
COMMON RESTART, SAMPLE()
'
'if returning from chained.abc then terminate demo
IF RESTART THEN GOTO TERMINATE
'
'set restart to false
RESTART = 0
'dim and load sample array
DIM SAMPLE(3): SAMPLE(1) = 10: SAMPLE(2) = 100: SAMPLE(3) = 1000
'
'verify that we are in main.exe
CLS
LOCATE 10, 20: PRINT "THIS IS MAIN PROGRAM"
'
'pause for keystroke
WHILE INKEY$ = "": WEND
'
'pass control from main.exe to chained.abc
CHAIN "CHAINED.ABC"
'
'end chaining demo
TERMINATE:
CLS
LOCATE 1, 1
END
'****************************************************************************
'****************************************************************************
'** Chained.bas
*************************************************************
'
'The executable being chained to.
'This technique will only work by compiling without the /o option.
'Be sure to rename chained.exe to chained.abc after compiling and
linking.
'
DEFINT A-Z
OPTION BASE 1
REM $DYNAMIC
COMMON RESTART, SAMPLE()
'
'verify that we are in chained.abc
LOCATE 10, 20: PRINT "THIS IS CHAINED PROGRAM"
LOCATE 12, 20: PRINT USING "####"; SAMPLE(1)
LOCATE 13, 20: PRINT USING "####"; SAMPLE(2)
LOCATE 14, 20: PRINT USING "####"; SAMPLE(3)
'
'pause for keystroke
WHILE INKEY$ = "": WEND
'
'set restart to true
RESTART = -1
'
'return to calling program
CHAIN "MAIN.EXE"
'****************************************************************************
Answer 4
~~~~~~~~~~~~~~
[ By CaressOfSteel*prodigy.net. ]
To chain something it must be in (interpreted) text mode and you can have
COMMON variables between the host and the chained program (hence you can only
chain .bas files, even if they have a different extension)
--- Remark by Thomas Antoni: That's definitely not correct. Also EXE
programms generated by QuickBasic can be chained !!
Try the following:
'---1.bas-------------
PRINT 1
CHAIN "2"
'---2.bas------------
COMMON a
a = 100
PRINT 2, a
CHAIN "3"
---3.bas------------
COMMON a
PRINT 3, a
*** ... and how to jump forward and backward from module to module?
I'd say you're right on track with the CHAIN command. It seems to be your
best option. Keep in mind, however, that starting another program with "chain"
removes your original program from memory, though it would certainly be possible
to chain right back to that program, should you need to. Also, should you need
to pass information from one program to the next, you will need to define any
such pieces of information as "Common" variables. For example, "Common Var1,
Var2, Arry1(), Arry2()" would allow the data in these variables & arrays to
pass to the next program. The only other thing I can think of is perhaps using
"Shell", though that could be messy.
For example, if you wanted to run another Qbasic program with shell, you'd
need to write something like:
Shell "Qbasic /run nextprog.bas"
The upside is that you can run ANY program, even non-qbasic programs, and
most
dos commands like DIR, using the shell command. That's about all I have for
you, hope it helps! Any questions, feel free to contact me at:
*** What's the difference between CHAIN and RUN?
If this IS Qbasic you're talking about, and not Quickbasic (or if it is
QuickBasic and you don't plan on ever compiling your code) you can use the CHAIN
or RUN command. CHAIN is used to move to a new file without closing any of the
files or clearing any of the COMMON variables. RUN is used if you want to run a
new totally independant file requiring no common variables or open files from
the original program. Oh, in QuickBasic you CAN compile using the chain command,
but only if you use BRUN45.EXE. Stand-alone format will not function
correctly.
CHAIN "PROGRAM.BAS"
RUN "PROGRAM.BAS"
Answer 5
~~~~~~~~~~~~
[By Michael Gudzinowicz ( bg174*FreeNet.Carleton.CA ), Oct. 1,
1997 ]
*** How to use COMMON variables in CHAINed EXE programs?
Question
My problem is very simple: I know there's a way to pass variables to another
program called with the CHAIN instruction,but I don't know how.I think COMMON
SHARED command could work well,but in which way?
Answer
To do that, you would define the variables in each program, and list them
after the COMMON statement. An example follows.
If you are running the chain command in the QBasic/QB/QBX environment using
"F5", the program will chain from one .bas file to another. To do that, replace
the CHAIN "program1.exe" and CHAIN "program2.exe" with CHAIN "program1.bas" and
CHAIN "program2.bas".
If you want to compile the programs, compile them to use the runtime module
option, "EXE requiring BRT". See the help file for details on the CHAIN command
and compiling instructions, and the COMMON statement.
' Program1.bas - compile with the "EXE requiring BRT" option.
'
DIM num(1 TO 1000) AS INTEGER
COMMON num() AS INTEGER
'
FOR a = 1 TO 1000
num(a) = a
PRINT "Program #1: ", num(a)
NEXT
'
PRINT "Press a key to start Program #2 or Q to quit."
SLEEP
IF INKEY$ = LCASE$("Q") THEN
END
ELSE
CHAIN "program2.exe"
END IF
END
' Program2.bas - compile with the "EXE requiring BRT" option.
'
DIM num(1 TO 1000) AS INTEGER
COMMON num() AS INTEGER
'
FOR a = 1 TO 1000
PRINT "Program #2: ", num(a)
NEXT
'
PRINT "Press a key to start Program #1 or Q to quit."
SLEEP
IF INKEY$ = LCASE$("Q") THEN
END
ELSE
CHAIN "program1.exe"
END IF
END
Antwort 6
~~~~~~~~~~~~~~~~
[ von juergen im QB-Forum, 17.11.2002 ]
*** Problem gemeinsam benutzte Variablen in gechainten
Programmen
Ich habe mich über die Aufteilung eines großen Programms mit dem CHAIN-Befehl
in der QB-MonsterFAQ und im Tutorial QBhelp informiert. Das ist ja alles schön
und gut, aber ich check das
mit der Variablenliste zum Übergeben von Daten an das gechaimte Teilprogramm
nicht so ganz. Kann mir jemmnd ein ganz einfaches CHAIN-Beispiel mit
Variablenübergabe geben?
*** Lösung
Der CHAIN-Befehl ist sehr penibel was die Übergabe von Variablen an ein
anderes Programm betrifft. Wenn Du im Hauptprogramm z.B. die Variable ARTNR als
Longinteger-Wert hast, dann muß die diese Variable ARTNR& als Longinteger
sowohl im aufrufenden als auch im aufzurufenden Programm an exakt derselben
Stelle mit COMMON [SHARED] ARTNR& deklariert werden. Das optionale SHARED
bewirkt, dass die Variable auch in allen SUBs und FUNCTIONs bekannt ist.
Bei mir klappte das auch nicht ganz so wie ich wollte. Mit einem Trick kann
man das Ganze folgendermaßen umgehen: Schreibe die Werte, die Du übergeben
willst, in eine -> Random-Datei. Dann rufe mit RUN das Folgeprogramm auf,
öffne die Randomdatei und lies die Werte wieder in die Variablen ein. Gut, die
Variablen musst Du auch im Folgeprogramm definieren, aber die Daten kommen
garantiert an, was bei CHAIN und COMMON wirklich nicht immer klappt. Außerdem
wirft der RUN-Befehl das 1. Programm aus dem Speicher und Du hast mehr Platz zur
Verfügung. Wenn Du das 2. Programm abgearbeitet hast, kannst Du über denselben
Weg wieder das 1. Programm über die Randomdatei aufrufen. Bei mir klappt das
ganz prima. Ich habe eine Artikelstammverwaltung zu der 5 Programmsegmente
gehören und die handle ich über diese Routine. Als nimm lieber RUN statt CHAIN.
CHAIN ist manchmal zum Haareausraufen!
Answer 7
~~~~~~~~~~~~~~~
[ from the QB-Forum at www.network54.com/Forum/ ]
*** Question
How do I use the CHAIN command?
*** Answer
Install these two programs in the current directory and issue command QBASIC
/RUN P1
'---- Program p1.bas
COMMON a$
CLS
PRINT "This is program one"
PRINT "The shared value is "; CHR$(34); a$; CHR$(34)
INPUT "New Shared Value, or null to stop"; a$
IF a$ = "" THEN SYSTEM
CHAIN "p2.bas"
'---- Program p2.bas
COMMON a$
CLS
PRINT "This is program two"
PRINT "The shared value is "; CHR$(34); a$; CHR$(34)
INPUT "New Shared Value, or null to stop"; a$
IF a$ = "" THEN SYSTEM
CHAIN "p1.bas"
[ The QBasic-MonsterFAQ --- Start Page: www.antonis.de/faq ]