Die Grundlagen der Windows-Programmierung

PowerBASIC for Windows erzeugt echte 32-bit Windows-Programme mit den für Windows üblichen grafischen Bedienoberflächen mit Pull-Down-Menüs, Eingabefeldern, Schaltflächen, Kontrollkästchen und vielen weiteren Windows-Steuerelementen. Anstelle der einfachen unter PB/DOS genutzten Befehle (LOCATE, PRINT, etc.) stehen unter PB/WIN weitaus leistungsfähigere Befehle zur Verfügung. Sehr wesentlich ist aber, dass sich unter Windows der Programmaufbau grundlegend unterscheidet, zumindest wenn man mit PB/WIN Programme mit Windows-Bedienoberfläche entwickeln möchte.

Umsteiger von PB/DOS sollten sich also zunächst einige Stunden Zeit nehmen, um das Konzept der ereignisgesteuerten Programmierung zu verstehen. Beim ersten Kontakt mit der Windows-Programmierung scheint dies immer die größte Hürde zu sein.

Es ist zunächst nicht notwendig, genau zu verstehen, warum diese ungewohnte Art der Programmierung zwingend geboten ist. Überhaupt sollte man als Einsteiger auf keinen Fall den Ehrgeiz haben, alles verstehen zu wollen. Zu groß wäre die Gefahr, nicht nur das neu angeschaffte PB/WIN, sondern auch den Vorsatz zum Einstieg in die Windows-Programmierung frustriert in die Ecke zu legen, noch bevor die ersten kleinen funktionierenden Programme die beim Programmieren so wichtige Begeisterung wachsen lassen.

Die folgende Anleitung soll also möglichst schnell zum ersten kleinen Windows-Programm hinführen, wobei wir uns nicht mit einer einfachen MessageBox mit der Anzeige von "Hello World!" begnügen wollen.
 

Also frisch ans Werk ....

Die Installation von PB/WIN hat noch nie ein Problem verursacht. CD-ROM rein und die Installation startet automatisch mit den üblichen und bekannten Schritten. Erforderlich ist die Eingabe der Seriennummer, welche in der Rechnung bzw. auf dem rückseitigen Aufkleber der CD-Hülle zu finden ist.

Bereits wenige Augenblicke später sollte man die PB/WIN IDE auf dem Bildschirm zu sehen bekommen. Ganz rechts in der Symbolleiste findet sich das Symbol (Handbuch mit Fragezeichen) zum Aufruf der Online-Hilfe. Natürlich sollte sich jeder Neuanwender zunächst zumindest einen Überblick über die vorhandenen Themen verschaffen.

Für Umsteiger von PowerBASIC/DOS (oder ähnlichen Programmiersprachen) könnte eine Durchsicht von "Command Summary" interessant sein, um gleich einmal festzustellen, welche längst vertrauten Befehle und Funktionen vorhanden sind und welche Befehle vollkommen unbekannt erscheinen. Ganz wichtig ist der Abschnitt "Data Types". Zumindest die wichtigsten Datentypen müssen jedem Programmierer unbedingt geläufig sein.

Glücklicherweise sind zumindest den Umsteigern von DOS schon viele Themen bestens bekannt. Berechnungen und Vergleichsoperationen, der Umgang mit Dateien, WHILE/WEND oder IF/THEN/ELSE Strukturen und die vielen Funktionen im Zusammenhang mit Stringvariablen sind entweder vollkommen gleich geblieben oder haben sich nur wenig geändert. Natürlich sind auch neue Funktionen hinzugekommen. Man muss aber nicht alle neuen Möglichkeiten sofort ausschöpfen.

Den Abschnitt "The PowerBASIC COM Browser" können Sie jetzt überspringen und eine deutliche Mehrheitl der PowerBASIC-Nutzer wird sich auch in Zukunft niemals damit beschäftigen müssen. Auch die Ausführungen zu sehr speziellen Themen wie "Serial Communications", "TCP and UDP Communications" und "The Inline Assembler" werden viele Programmierer vermutlich niemals benötigen.
 

Dynamic Dialog Tools (DDT)

Empfehlen möchte ich aber im Abschnitt "Programming Reference" die Erläuterungen zu "Dynamic Dialog Tools (DDT)". Bereits in der Übersicht "Command Summary" findet man in der Gruppe "Dynamic Dialog Tools" etwa einhundert zugehörige Befehle und Funktionen beschrieben. Viele der DDT-Anweisungen beginnen mit den Schlüsselwörtern CONTROL oder DIALOG.

Interessant wird es beim Mausklick auf das "leere Blatt" (Create new file) ganz links in der Symbolleiste. Es erscheint ein vierzeiliges Programmgerüst und sogleich macht man Bekanntschaft mit einem weiteren kleinen Unterschied gegenüber PB/DOS. Ausführbarer Code muss sich bei PB/WIN immer innerhalb einer Funktion befinden. Die Funktion PBMAIN hat dabei eine besondere Bedeutung. Die Ausführung des Programmes beginnt mit der Funktion PBMAIN als Einsprungpunkt und das Programm wird beendet beim Erreichen der zugehörigen Zeile END FUNCTION oder notfalls frühzeitig mittels EXIT FUNCTION. Den bei PB/DOS benutzen Befehl END zum Beenden der Programmausführung gibt es unter PB/WIN nicht.

#DIM All sollte man sich angewöhnen, weil es zu mehr Disziplin zwingt und so manchen Fehler vermeiden hilft

Natürlich könnten Sie jetzt einige Variablen deklarieren und schnell einmal ausprobieren, wie lange eine FOR/NEXT-Schleife mit zehnmillionen Durchläufen dauert. Als Erfolgssignal könnten Sie eine Meldung als MessageBox ausgeben.

Also ein erster Versuch beispielsweise in der folgenden Form:


#COMPILE EXE
#DIM ALL

FUNCTION PBMAIN () AS LONG

   LOCAL I, Z AS LONG
   Z&=0
   FOR I&= 1 TO 10000000
       Z&=Z& + 2
   NEXT I&
   MSGBOX "Z ="+STR$(Z&),,"Ergebnis"

END FUNCTION
 

Sofort nach ALT-R und "Compile and Execute" sollten Sie das Ergebnis am Bildschirm sehen. Falls nicht, hat sich einen kleiner Tippfehler eingeschlichen und Sie finden eine entsprechende Meldung im unteren Bildschirmbereich. Sie können übrigens die IDE über "Window"-"Options..." nach Ihrem persönlichen Geschmack etwas anpassen, was Sie sofort machen sollten, wenn Sie die voreingestellte Schriftgröße als zu klein empfinden.
 

Jetzt geht es aber richtig los ....

Für ein echtes Windows-Programm brauchen wir zunächst einmal ein Fenster, welches wirklich leicht zu erzeugen ist. Es reichen als Mindestvorgabe die Breite und Höhe des Fensters sowie der Text für die Titelleiste und eine DWORD-Variable, in der Windows die Kennziffer (Handle) des neuen Fensters ablegt. Mit dieser Kennziffer ist das neue Fenster in allen weiteren Aktionen klar ansprechbar. Zusätzlich könnte man noch die Positionierung der linken oberen Ecke angeben, falls keine mittige Ausrichtung am Bildschirm gewünscht ist.


#COMPILE EXE
#DIM ALL

FUNCTION PBMAIN () AS LONG

   LOCAL hDlg AS DWORD
   DIALOG NEW 0, "Es geht los ...", , , 400, 300, TO hDlg
   DIALOG SHOW MODAL hDlg

END FUNCTION
 

Was man jetzt nach "Compile and Execute" zu sehen bekommt, ist bestimmt noch nicht beeindruckend. Ein sehr einfaches Fenster mit keinen weiteren Möglichkeiten. Sie können das Fenster höchstens noch mit der linken Maustaste an der Titelleiste festhalten und über den Bildschirm ziehen. Zum Glück hilft Windows mit der Tastenkombination ALT-F4 beim Schließen des Fensters.

Wenn Sie Lust haben, können Sie sich mit der weiteren Gestaltung dieses Fensters etwas beschäftigen. In der Dokumentation zu DIALOG NEW findet man eine Anzahl von möglichen Stilparametern. Also einmal auf "NEW" oder "DIALOG" klicken und danach die Funktionstaste F1 drücken. Es erscheint eine Auswahl mit den gefundenen Themen - wählen Sie "DIALOG NEW statement".

In der Erläuterung zu style& findet man eine Anzahl von Stilparametern, welche nicht nur das Aussehen, sondern auch das Verhalten des Fensters bestimmen. Eine derart große Auswahl an Möglichkeiten für eine auf den ersten Blick einfache Angelegenheit ist typisch für Windows. Auch hier empfehle ich, zunächst nicht zuviel Ehrgeiz zu zeigen. Wer bei der Windows-Programmierung immer sofort alles verstehen möchte, kommt nur sehr, sehr langsam zum gewünschten Ergebnis.

Ergänzen wir aber trotzdem in der Zeile DIALOG NEW den Stilparameter %WS_SYSMENU wie folgt:

DIALOG NEW 0, "Es geht los ...", , , 400, 300, %WS_SYSMENU TO hDlg

Beim erneuten Kompilieren erhalten wir jetzt aber eine Fehlermeldung:

Error 460 Undefined equate
A named constant was used in without being defined.

Alles klar - wir benutzen die bezeichnete Konstante %WS_SYSMENU und haben dieser Konstante in unserem Programm noch keinen Wert zugewiesen.

Windows benutzt für %WS_SYSMENU den Wert &H00080000. Es wäre jedoch sehr mühsam, wenn man für alle denkbaren Stilparameter und für alle sonstigen von Windows benutzten Vorgaben immer selbst die zugehörigen Werte herausfinden müsste. Diese Arbeit haben glücklicherweise schon andere fleißige Helfer erledigt. Durch ein einfaches Einfügen der Zeile

#INCLUDE "WIN32API.INC"

nach der Zeile #DIM ALL können wir ohne weitere Mühe über diesen Schatz an Vorgaben verfügen.

Nach dem nächsten Kompilieren sollte also wieder alles funktionieren. %WS_SYSMENU hat uns links und rechts in die Titelleiste jeweils ein Icon zum Schließen des Fensters gezaubert.
 

Wichtige Windows-Steuerelemente

Zu den am meisten genutzten Windows-Steuerelementen zählen sicher die Schaltflächen und die Editfenster, welche ein- oder mehrzeilig sein können. Auch die einfachen Textfenster werden häufig verwendet. Diese sind nur zur Textausgabe oder zur Beschriftung anderer Elemente gedacht und sind deshalb besonders einfach zu handhaben.

Wir ergänzen also unser kleines Beispiel mit einer kleinen Auswahl dieser einfachen Steuerelemente:


#COMPILE EXE
#DIM ALL
#INCLUDE "WIN32API.INC"

%IDC_LABEL1  = 1001
%IDC_LABEL2  = 1002
%IDC_TEXTBOX1 = 1003
%IDC_LABEL3  = 1004
%IDC_TEXTBOX2 = 1005
%IDC_LABEL4  = 1006
%IDC_TEXTBOX3 = 1007
%IDC_BUTTON1 = 1008

FUNCTION PBMAIN () AS LONG

   LOCAL hDlg AS DWORD
  
   DIALOG NEW 0,"Flächenberechnung", , ,320,190,%WS_SYSMENU TO hDlg
  
   CONTROL ADD LABEL,   hDlg, %IDC_LABEL1, _
       "Wir berechnen die Fläche eines Rechtecks:", 15, 15, 355, 10
   CONTROL ADD LABEL,   hDlg, %IDC_LABEL2, "Länge", 15, 50, 50, 10
   CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX1, " ", 75, 45, 80, 13
   CONTROL ADD LABEL,   hDlg, %IDC_LABEL3, "Breite", 15, 80, 50, 10
   CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX2, " ", 75, 75, 80, 13
   CONTROL ADD LABEL,   hDlg, %IDC_LABEL4, "Fläche", 15, 110, 50, 10
   CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX3, " ", 75, 105, 80, 13
   CONTROL ADD BUTTON,  hDlg, %IDC_BUTTON1,"Berechnung",195,104,105,15
   CONTROL ADD BUTTON,  hDlg, %IDCANCEL,"Programmende",195,134,105,15
      
   DIALOG SHOW MODAL hDlg

END FUNCTION
 

Im Quellcode fällt zunächst die Liste der bezeichneten Konstanten auf. Genauso wie das Fenster selbst mit dem Handle hDlg benötigen auch die im Dialog-Fenster eingebundenen Elemente jeweils eine eindeutige Kennung und über die Kennziffer hDlg natürlich auch die Zuordnung zum beherbergenden Fenster (parent). Die gewählte Namensgebung für die Konstanten hat sich allgemein bewährt und wird in dieser Art auch von PB/Forms benutzt.
 

Nach dem Kompilieren macht das Dialog-Fenster schon einen ganz netten optischen Eindruck. Wer sich daran stört, dass für die Eingabe von Länge und Breite auch Buchstaben anstelle von Ziffern möglich sind, kann dies leicht durch Hinzufügen des Stilparameters %ES_NUMBER ändern:

CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX1, " ",75,47,80,14, %ES_NUMBER

Doch warum ändert sich dadurch das Aussehen des Eingabefeldes? Der optische Eindruck eines etwas abgesenkten Eingabefeldes ist verloren gegangen. Ein Blick in die Dokumentation löst das Rätsel. Wenn keinerlei Stilparameter angegeben sind, wählt PowerBASIC sinnvolle Standard-Vorgaben. Für style& sind das die Werte %WS_TABSTOP, %WS_BORDER, %ES_LEFT und %ES_AUTOHSCROLL. Bei exstyle& sind es die erweiterten Parameter %WS_EX_CLIENTEDGE und %WS_EX_LEFT. Wird allerdings durch den Programmierer ein bestimmter abweichender Stil-Parameter gewünscht und deshalb bei style& oder exstyle& ein Eintrag gemacht, so sind alle zusätzlich erforderlichen Stilparameter selbst zu ergänzen. Diese Regelung ist sinnvoll, da PowerBASIC nicht eigenmächtig eine individuelle Gestaltung durch andere Eigenschaften verfälschen darf.

Mit folgenden Ergänzungen schaut es gleich wieder besser aus:

CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX1, " ", 75, 45, 80, 13, _
   %WS_TABSTOP OR %WS_BORDER OR %ES_NUMBER, %WS_EX_CLIENTEDGE

Noch eine kleine Ergänzung macht Sinn. Nachdem in der 3. Textbox keinerlei Benutzer-Eingaben notwendig sind, könnte man dies durch den entsprechenden Stilparameter %ES_READONLY verhindern.

CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX3, " ", 75, 105, 80, 13, _
     %WS_BORDER OR %ES_READONLY, %WS_EX_CLIENTEDGE


Für unseren Zweck sollte das Aussehen des Dialog-Fensters jetzt passen:

einstieg

Es gäbe natürlich noch viele weitere Möglichkeiten, das Windows-Dialogfenster und die Steuerelemente anders zu gestalten. Versuchen Sie auch hier nicht alle Besonderheiten sofort zu verstehen und alle Möglichkeiten zu beherrschen. Wissen sollte man vielleicht noch, dass sich leider nicht alle Windows-Versionen absolut gleich verhalten. So kann etwa die Positionierung der Steuer-Elemente geringfügig abweichen. Setzen Sie deshalb die Steuerelemente nicht zu knapp an den Rand des Dialogfensters.

Bei diesem Beispiel ist die Eingabe der Werte sehr schnell erledigt. Werden in einer anderen Situation umfangreiche Eingaben erwartet, möglicherweise sogar mehrzeiliger Text, so ist nützlich, dass man nach gewohnter Windows-Art die Eingabefelder mit Text aus der Zwischenablage füllen kann. Dies ist einer der Vorteile, die Windows automatisch ohne weiteren Programmieraufwand zur Verfügung stellt.
 

Das Fenster rechnet nicht !

Selbstverständlich müssen wir noch die Formel für die Rechteckfläche irgendwo im Programm unterbringen. Entsprechend der Gestaltung unseres Windows-Dialogfensters sollte eine Berechnung nach dem Anklicken der Schaltfläche "Berechnung" erfolgen und danach das Ergebnis in das Feld "Fläche" geschrieben werden.

Wir benötigen also eine Möglichkeit, das Anklicken einer Schaltfläche zu erkennen, um daraufhin die gewünschte Berechnung einzuleiten. Gerade dies ist unter Windows kein großes Problem. Windows überwacht alle möglichen Aktivitäten und meldet eine Vielzahl von Ereignissen in genau festgelegter Form. Die Benachrichtigung enthält immer die Kennziffer (Handle) des zugehörigen Dialogfensters, eine Kennziffer für die Art der Nachricht und zwei weitere Werte, deren Bedeutung von der Art der Nachricht abhängt.

Unser Programm muss also nur auf ausgewählte Nachrichten reagieren, welche auf für uns interessante Ereignisse hinweisen. Bei der Programmierung im PowerBASIC-DDT-Stil - also mit den Dynamic Dialog Tools - erfolgt die Erkennung von Windows-Nachrichten und die entsprechende Auswertung innerhalb einer CALLBACK FUNCTION. In der Literatur (z.B. C++) wird eine Funktion mit dieser Aufgabe übrigens meist als Window-Prozedur bezeichnet. Die CALLBACK FUNCTION wird entweder einem einzelnem Steuerelememt (z.B. einer Schaltfläche) zugeordnet oder sie ist für die Auswertung aller Nachrichten eines Dialogfensters zuständig.

Eine CALLBACK FUNCTION dürfen Sie niemals über eine Befehlszeile (z.B. mit CALL) direkt aus Ihrem Programm heraus aufrufen. Der Aufruf erfolgt immer über Windows selbst. Nach
der Ausführung der CALLBACK FUNCTION geht die Kontrolle immer zunächst wieder zurück
an Windows, bevor weitere Teile Ihres eigenen Quellcodes abgearbeitet werden. In unserem Beispiel nennen wir die CALLBACK FUNCTION DlgProc. Für die Verknüpfung mit dem Dialogfenster müssen wir die folgende Zeile noch etwas ergänzen:

DIALOG SHOW MODAL hDlg, CALL DlgProc

Dadurch ist die CALLBACK FUNCTION DlgProc für die Auswertung aller Nachrichten des Dialogfensters mit der Kennziffer (Handle) hDlg zuständig.

Annähernd jede in einem Windows-Programm ausgeführte Aktion ist eine Reaktion auf eine Windows-Nachricht, welche aufgrund eines bestimmten Ereignisses generiert wurde. Dieses Konzept der ereignisgesteuerten Programmierung ist ganz wesentlich für die Windows-Programmierung und zwar unabhängig mit welcher Programmiersprache man arbeitet.

Übrigens kann ein Ereignis die vielfältigsten Ursachen haben. Nicht nur ein Mausklick oder ein Tastendruck oder die Bewegung der Maus lösen entsprechende Windows-Nachrichten aus. Auch das Verstreichen einer bestimmten Zeitspanne kann ein zeitabhängiges Ereignis auslösen. Zusätzlich gibt es noch die von Windows automatisch generierten Nachrichten und natürlich auch die durch das Programm selbst erzeugten Nachrichten. Allein in der Include-Datei WIN32API.INC findet man mehr als einhundert bezeichnete Konstanten mit möglichen vordefinierten Ereignissen.

Damit sich niemand von diesen vielen Möglichkeiten erdrückt fühlt, möchte ich auch hier wieder darauf hinweisen, was sich Einsteiger immer vor Augen halten sollten: Man muss nicht alle Hintergründe verstehen, um brauchbare einfache Windows-Programme entwickeln zu können. Kaum ein Programmierer ist mit allen Möglichkeiten vertraut. Folgen Sie zunächst dem immer gleichen Schema und Sie werden Windows-Programme mit dem allgemein üblichen Aussehen schon nach kurzer Einarbeitungszeit erstellen können.
 

Wie ist die CALLBACK FUNCTION aufgebaut?

Die CALLBACK FUNCTION wird durch Windows aufgerufen, sobald eine Nachricht aufgrund eines für uns noch unbekannten Ereignisses vorliegt.

Weil viele Ereignisse für eine bestimmte Aufgabenstellung vollkommen ohne Bedeutung sind (z.B. in unserem Beispiel die Bewegung der Maus), muss innerhalb der CALLBACK FUNCTION zuerst einmal festgestellt werden, ob es sich um eine für das Programm interessante Nachricht handelt.

PowerBASIC stellt eine Anzahl von Funktionen (CBMSG, CBCTL, CBCTLMSG, CBHNDL, CBLPARAM, CBWPARAM) zur Verfügung, mit deren Hilfe man die Bedeutung einer Nachricht relativ leicht herausfinden kann. Üblicherweise benutzt man SELECT-Strukturen, um sich den erwarteten Ereignissen zu nähern. Erkennt man das erwartete Ereignis (in unserem Beispiel das Anklicken der Schaltfläche "Berechnung"), muss nur noch die dafür vorgesehene Befehlsfolge ausgeführt werden.

In einfachen Windows-Programmen geht es überwiegend um Nachrichten vom Typ %WM_COMMAND, welche immer bei Ereignissen im Zusammenhang mit Windows-Steuerelementen ausgelöst werden. Wird also in unserem Beispiel eine der beiden Schaltflächen angeklickt, wird als Folge in jedem Fall eine %WM_COMMAND-Nachricht übermittelt. Für die Erkennung der Nachricht innerhalb einer CALLBACK FUNCTION hilft uns PB/WIN mit der Funktion CBMSG.

Im nächsten Schritt untersuchen wir, welches Steuerelement die %WM_COMMAND-Nachricht verursacht hat. PB/WIN bietet dafür die Funktion CBCTL, welche wir ebenfalls über eine SELECT-Struktur einsetzen. In unserem Dialogfenster gibt es nur zwei Schaltflächen. Innerhalb der SELECT-Struktur setzen wir für jede Schaltfläche eine CASE-Bedingung.

Unter idealen Bedingungen könnten wir mit diesem Minimum an Programmschritten schon auf das Anklicken der Schaltfläche reagieren. Windows wäre aber nicht Windows, wenn es nicht Situationen gäbe, welche noch eine weitere Prüfung notwendig machen. Man sollte deshalb immer sicherstellen, ob auch wirklich das vermutete Ereignis stattgefunden hat. Mit der Funktion CBCTLMSG können wir überprüfen, ob tatsächlich die Schaltfläche gedrückt wurde, d.h. ob mit der Nachricht der entsprechende Wert (%BN_CLICKED) übergeben wurde.

Wer hier wegen der Notwendigkeit dieser weiteren Prüfung Zweifel hegt, kann diese im nachfolgenden Listing testweise weglassen und bei der Schaltfläche "Berechnung" die Stilparameter %BS_NOTIFY und %WS_TABSTOP ergänzen:

CONTROL ADD BUTTON, hDlg, %IDC_BUTTON1, "Berechnung", _
       195, 104, 105, 15 , %BS_NOTIFY OR %WS_TABSTOP

Üblicherweise springt man im Dialog-Fenster mit der TAB-Taste von Steuerelement zu Steuerelement. Ist die Schaltfläche mit %BS_NOTIFY ausgestattet, wird bereits ein Ereignis gemeldet, sobald die Schaltfläche den Fokus erhält. %WS_TABSTOP ist deshalb notwendig, weil sonst die Schaltfläche einfach übersprungen würde.

Es gibt sogar sehr seltene Situationen, für die noch eine weitere zweite Prüfung notwendig ist. Um ganz sicher zu gehen, sollte man deshalb immer noch als zweite Bedingung CBCTLMSG=1 hinzufügen.

In gleicher Weise behandeln wir noch die Schaltfläche für das Programmende. Dabei kommt die Funktion CBHNDL zum Einsatz, welche uns die Kennziffer (Handle) des Dialogfensters mitteilt. Diese Kennziffer wird für den Befehl DIALOG END benötigt, um das Dialog-Fenster zu beenden.

Der von CBHNDL gelieferte Wert ist natürlich identisch mit der innerhalb der FUNCTION PBMAIN über den Befehl DIALOG NEW zugeteilten Kennziffer hDlg. Innerhalb der CALLBACK FUNCTION steht hDlg jedoch nicht zur Verfügung, weil hDlg nur als lokale Variable innerhalb der FUNCTION PBMAIN deklariert wurde. In manchen Listings wird hDlg als globale Variable deklariert und steht dadurch mit der immer gleichen Bezeichung im ganzen Programm zur Verfügung. Nachdem es in einfachen Programme oftmals ohnehin nur ein einziges Dialogfenster gibt, wird diese Methode von manchen Programmierern bevorzugt. Andere Programmierer empfinden die Benutzung von globalen Variablen (ähnlich dem Befehl GOTO) als schlechten Programmierstil und lehnen die Verwendung von GLOBAL grundsätzlich ab.

So oder so haben wir das ereignisgesteuerte Programmgerüst jetzt fertig und der Quellcode sollte zwischendurch auch einmal getestet werden:


#COMPILE EXE
#DIM ALL
#INCLUDE "WIN32API.INC"

%IDC_LABEL1  = 1001
%IDC_LABEL2  = 1002
%IDC_TEXTBOX1 = 1003
%IDC_LABEL3  = 1004
%IDC_TEXTBOX2 = 1005
%IDC_LABEL4  = 1006
%IDC_TEXTBOX3 = 1007
%IDC_BUTTON1 = 1008


CALLBACK FUNCTION DlgProc()

   SELECT CASE AS LONG CBMSG
       CASE %WM_COMMAND

           SELECT CASE AS LONG CBCTL
                 CASE %IDC_BUTTON1
                   IF CBCTLMSG = %BN_CLICKED OR CBCTLMSG = 1 THEN
                       MSGBOX "%IDC_BUTTON1", %MB_TASKMODAL
                   END IF
                   FUNCTION = 1

                 CASE %IDCANCEL
                   IF CBCTLMSG = %BN_CLICKED OR CBCTLMSG = 1 THEN
                       DIALOG END CBHNDL, 0   '<- Programmende
                   END IF
                   FUNCTION = 1

             END SELECT

   END SELECT

END FUNCTION


FUNCTION PBMAIN () AS LONG

   LOCAL hDlg AS DWORD

   DIALOG NEW 0, "Flächenberechnung", , ,320,190,%WS_SYSMENU TO hDlg

   CONTROL ADD LABEL,   hDlg, %IDC_LABEL1, _
       "Wir berechnen die Fläche eines Rechtecks:",15,15,355,10
   CONTROL ADD LABEL,   hDlg, %IDC_LABEL2, "Länge", 15, 50, 50, 10
   CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX1, " ", 75, 45, 80, 13, _
       %WS_TABSTOP OR %WS_BORDER OR %ES_NUMBER, %WS_EX_CLIENTEDGE
   CONTROL ADD LABEL,   hDlg, %IDC_LABEL3, "Breite", 15, 80, 50, 10
   CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX2, " ", 75, 75, 80, 13, _
       %WS_TABSTOP OR %WS_BORDER OR %ES_NUMBER, %WS_EX_CLIENTEDGE
   CONTROL ADD LABEL,   hDlg, %IDC_LABEL4, "Fläche", 15, 110, 50, 10
   CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX3, " ", 75, 105, 80, 13, _
       %WS_BORDER OR %ES_READONLY, %WS_EX_CLIENTEDGE
   CONTROL ADD BUTTON, hDlg, %IDC_BUTTON1, "&Berechnung",195,104,105,15
   CONTROL ADD BUTTON, hDlg, %IDCANCEL, "Programm&ende",195,134,105,15

   DIALOG SHOW MODAL hDlg, CALL DlgProc

END FUNCTION
 

Dem sehr aufmerksamen Leser sind vermutlich einige kleinere Ergänzungen aufgefallen?

In der Zeile CALLBACK FUNCTION DlgProc() habe ich das Klammernpaar () angefügt, weil damit zum Ausdruck kommt, dass die Funktion einen Wert übergibt. Für die CALLBACK FUNCTION ist das zwar nicht unbedingt notwendig, aber schaden kann die Einhaltung der sonst üblichen Regeln auch nicht.

Nach der erfolgreichen Verarbeitung einer %WM_COMMAND-Nachricht sollte die CALLBACK FUNCTION immer den Wert TRUE (also nicht Null) an Windows zurückgeben, um eine weitere Behandlung durch Windows zu verhindern. Dies optimiert die Programmausführung und verhindert Fehler aufgrund mehrfacher Ereignisbehandlung.

Bei der Beschriftung der Schaltflächen habe ich jeweils ein "&" eingefügt. Dies ermöglicht das Auslösen der Schaltflächen durch die Tastenkombinationen ALT-B bzw. ALT-E
 

Rechteckfläche = Länge * Breite

Um das bisher erarbeitete Programmgerüst - welches in abgewandelter Form für viele weitere Aufgaben genutzt werden kann - möglichst übersichtlich und verständlich darzustellen, wurde die Berechnung zunächst zurückgestellt. Dort wo jetzt der Aufruf der MessageBox als Reaktion auf das Anklicken der Schaltfläche "Berechnung" zu finden ist, fügen wir die paar Zeilen ein, welche den eigentlichen Zweck unseres Programms ausmachen.

Das Konzept für die notwendigen Schritte ist schnell überlegt. Wir lesen zunächst die Werte für Länge und Breite aus den Eingabefeldern, berechnen die Rechteckfläche und schreiben das Ergebnis in das Feld "Fläche". Fertig!

Für das Lesen des Textes aus dem Eingabefeld benutzt man den PowerBASIC-Befehl

CONTROL GET TEXT hDlg, id& TO txt$

Um an die gewünschten Daten zu kommen benötigen wir wieder das Fenster-Handle und die Kennziffer des Editfeldes. Auch wenn es sich wie hier beim Inhalt um eine Zahl handelt, erhält man vom Steuerelement die Zeichenfolge immer als String. Zur Berechnung weisen wir den Wert einer numerischen Variable zu. Vielleicht ist es Ihnen beim kurzen Antesten schon aufgefallen, dass Windows in Verbindung mit %ES_NUMBER im Editfeld nur die Ziffern von 0 bis 9 erlaubt. Ein Dezimalkomma oder das Minuszeichen für negative Zahlen sind nicht vorgesehen. Stören Sie sich daran im Moment nicht! Es gibt andere Möglichkeiten, um eine kontrollierte Eingabe zu gestalten. Hier im Beispiel wollte ich in erster Linie den Umgang mit Stilparametern etwas näher bringen und sicher gibt es Aufgaben, wo man tatsächlich nur ganzzahlige Werte benötigt.

Wenn aber ohnehin keine Fließkommawerte bei der Eingabe erlaubt sind, können wir uns bei der Wahl des Variablentyps für eine ganzzahlige DWORD-Variable entscheiden. In einem echten Programm würde man natürlich noch prüfen, ob die Eingabedaten im erlaubten Bereich liegen. Zweifellos liegt bei Berechnungen unter PB/WIN gegenüber PB/DOS mehr Verantwortung beim Programmierer. Fehlermeldungen wie z.B. "Division durch Null" gibt es unter PB/WIN nicht. In einer sorgfältig programmierten Anwendung werden derartig Sonderfälle sowieso immer besonders behandelt.

Nach der Berechnung schreiben wir die Fläche in das vorgesehene Feld mit dem Befehl

CONTROL SET TEXT hDlg, id&, txt$

Im fertigen Quellcode zeigt sich ganz deutlich, dass der eigentliche Zweck unseres kleinen Programms in wenigen Zeilen erledigt wird. Der Großteil der Programmbefehle beschäftigt sich mit der Gestaltung des Dialogfensters und der Ereignisbehandlung. Wer aus wirtschaftlichen Gründen auf seine Arbeitszeit achten muss, sollte überlegen, ob er sich bei den zeitaufwändigeren Arbeitsschritten nicht besser vom zusätzlich angebotenen Werkzeug PowerBASIC/Forms helfen läßt.


#COMPILE EXE
#DIM ALL
#INCLUDE "WIN32API.INC"

%IDC_LABEL1  = 1001
%IDC_LABEL2  = 1002
%IDC_TEXTBOX1 = 1003
%IDC_LABEL3  = 1004
%IDC_TEXTBOX2 = 1005
%IDC_LABEL4  = 1006
%IDC_TEXTBOX3 = 1007
%IDC_BUTTON1 = 1008


CALLBACK FUNCTION DlgProc()
  LOCAL txt AS STRING
  LOCAL l, b, f AS DWORD

  SELECT CASE AS LONG CBMSG
   CASE %WM_COMMAND

     SELECT CASE AS LONG CBCTL
       CASE %IDC_BUTTON1
         IF CBCTLMSG = %BN_CLICKED OR CBCTLMSG = 1 THEN

           CONTROL GET TEXT CBHNDL, %IDC_TEXTBOX1 TO txt$
           l???=VAL(txt$)
           CONTROL GET TEXT CBHNDL, %IDC_TEXTBOX2 TO txt$
           b???=VAL(txt$)
           f???=l??? * b???
           CONTROL SET TEXT CBHNDL, %IDC_TEXTBOX3, STR$(f???)

         END IF
         FUNCTION = 1

       CASE %IDCANCEL
         IF CBCTLMSG = %BN_CLICKED OR CBCTLMSG = 1 THEN
           DIALOG END CBHNDL, 0   '<- Programmende
         END IF
         FUNCTION = 1

     END SELECT

  END SELECT

END FUNCTION


FUNCTION PBMAIN () AS LONG

  LOCAL hDlg AS DWORD

  DIALOG NEW 0, "Flächenberechnung", , ,320,190,%WS_SYSMENU TO hDlg

  CONTROL ADD LABEL, hDlg, %IDC_LABEL1, _
     "Wir berechnen die Fläche eines Rechtecks:", 15, 15, 355, 10
  CONTROL ADD LABEL,   hDlg, %IDC_LABEL2, "Länge", 15, 50, 50, 10
  CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX1, " ", 75, 45, 80, 13, _
     %WS_TABSTOP OR %WS_BORDER OR %ES_NUMBER, %WS_EX_CLIENTEDGE
  CONTROL ADD LABEL,   hDlg, %IDC_LABEL3, "Breite", 15, 80, 50, 10
  CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX2, " ", 75, 75, 80, 13, _
     %WS_TABSTOP OR %WS_BORDER OR %ES_NUMBER, %WS_EX_CLIENTEDGE
  CONTROL ADD LABEL,   hDlg, %IDC_LABEL4, "Fläche", 15, 110, 50, 10
  CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX3, " ", 75, 105, 80, 13, _
     %WS_BORDER OR %ES_READONLY, %WS_EX_CLIENTEDGE
  CONTROL ADD BUTTON, hDlg, %IDC_BUTTON1, "&Berechnung",195,104,105,15
  CONTROL ADD BUTTON,  hDlg, %IDCANCEL, "Programm&ende",195,134,105,15

  DIALOG SHOW MODAL hDlg, CALL DlgProc

END FUNCTION

[Startseite] [Compiler] [TOOLS] [FORUM] [Preisliste] [Kontakt]