% Content-encoding: UTF-8
\documentclass[ngerman]{article}
\usepackage[utf8]{inputenc}
\usepackage{multicol,babel}
\setcounter{secnumdepth}{0}
\setcounter{tocdepth}{0}

\renewcommand{\reftextbefore}{auf der vorherigen Seite}
\renewcommand{\reftextfacebefore}{auf der gegenüberliegenden Seite}
\renewcommand{\reftextafter}{auf der nächsten Seite}
\renewcommand{\reftextfaceafter}{auf der gegenüberliegenden Seite}

\begin{document}

% \renewcommand{\figurename}{Tabelle}

\title{Win32Forth — Wie ruft man damit Routinen \\\hspace*{\fill}von Windows auf?}
\ifx\shorttitle\undefined\else
\shorttitle{Win32Forth ruft Windows}
\fi
\author{Alex Mcdonald (Ins Deutsche übertragen von Michael Kalus)}
\maketitle

Es macht Mühe, sich in all die Routinen einzufuchsen, die Windows bietet. Aber es ist auch kein Buch mit sieben Siegeln. Wer sich also da herantasten will, tut gut daran, ein Werkzeug zu nehmen, das erste Erfolgserlebnisse sofort ermöglicht. Und einen dann auf dem Weg zur vollen Kontrolle über die CALLs auch nicht im Stich lässt. Mein Eindruck ist, dass Win32Forth (W32F) gut vermittelt, wie das Betriebssystem Windows für eigene Programme genutzt werden kann.

\begin{multicols}{2}
Dieses Forth von Tom Zimmer, das er der Öffentlichkeit schenkte und bis 2002 noch selbst betreut hat, wird seither von einer aktiven Gruppe von Benutzern weitergepflegt$^1$. So ist das W32F inzwischen zu einem erstaunlich umfangreichen System geworden. Neben der klassischen Forth--Konsole bietet es eine komfortable integrierte Entwicklungsumgebung (IDE) an, deren wichtigste Features sind: Syntax highlighting, Projektmanagement,
Dialog Editor, Debugger. Und mit der ForthForm lassen sich inzwischen Ein-- und Ausgabe--Fenster einfach zusammenklicken. Dabei ist ForthForm inzwischen ebenso Bestandteil der IDE geworden wie der
Projekt--Manager. Die Standalone--Versionen der beiden Programme
werden von der Gruppe nicht mehr weitergepflegt. Dies gilt auch für Toms
Editor WinView (bzw. WinEd). 

Um Berührungsängste abzubauen und einen Anfang zu finden, ist es vielleicht hilfreich, sich einmal das Grundlegende zu den DLLs aus der umfangreichen Dokumentation des W32F$^2$ heraus zu picken. Drum habe ich es ins Deutsche übersetzt. Die Entwicklergruppe war damit einverstanden, doch bat Dirk Busch um den Hinweis, dass einiges in der Dokumentation inzwischen von der aktuellen Entwicklung des W32F überholt worden ist. Fragen dazu werden gern aus der Gruppe beantwortet. Hingewiesen sei auch darauf, dass derzeit eine Betaversion der nächsten W32F Version 6.13 unter:
\url{http://www.schneider-busch.de/dirk/forth/download/w32f61301.exe}
zum Download bereitsteht. Ab dieser Version wird es ein nützliches Feature in Bezug auf DLLs geben. Im Menü \emph{Tools} der Konsole findet sich der Eintrag \emph{List DLL exports}, mit dem man sich die Namen der Funktionen, die von einer DLL exportiert werden, anzeigen lassen kann. (mka)

\section{Grundlegendes}

Es ist einfach, mit Win32Forth Windows--Aufrufe zu machen --- wenn man weiß, wie. Dieses Tutorial erklärt, welche Schlüsselworte für die Verbindung zu den DLLs benutzt werden, und gibt einige einfache Beispiele, um das zu veranschaulichen.

Dabei wird angenommen, dass du eine Version des Win32Forth benutzt, die CALL-- und PROC--Instruktionen bereits im Forthkern hat. Tippe .LIBS, um das zu prüfen. Die Bedeutung der Spalten wird weiter unten im Text noch erklärt werden; die Adressen und die Liste selbst mag auf deiner Maschine anders sein, das variiert je nach Betriebssystem und Win32Forth--Version.

\begin{verbatim}
.libs
Location Handle   Type Name
-------- -------- ---- -----------
000450D8 71710000 APP  COMCTL32.DLL
0002C91C 76B30000 APP  COMDLG32.DLL
0001794C 10000000 APP  WINCON.DLL
00017934 782F0000 APP  SHELL32.DLL
0001791C 7C2D0000 APP  ADVAPI32.DLL
00017908 77F40000 APP  GDI32.DLL
00013490 00D20000 APP  PRINT.DLL
0000E6D8 63180000 APP  SHLWAPI.DLL
0000BFA0 7C570000 KERN KERNEL32.DLL
0000B0FC 77E10000 APP  USER32.DLL
0000A970 00D40000 APP  CONSOLE.DLL
\end{verbatim}


\section{Grundlagen der DLLs}

DLLs (Dynamic Link Libraries) sind auch nur Programme auf deinem PC wie alle anderen, haben jedoch einige wenige grundlegende Unterschiede. 

DLLs haben gewöhnlich kein sichtbares Interface, also kein \emph{Fenster}. Und zudem können sich ganz verschiedene Prozesse eine DLL teilen. So liegt gewöhnlich nur eine Kopie davon im Speicher, ganz egal, wie viele Programme die dann benutzen. DLLs stellen vorgefertigte Funktionen zur Verfügung, die dann von allen anderen Programmen benutzt werden können. Und um sie effektiv zu verwenden, braucht man deren Dokumentation.



\section{Win32Forth--Worte dafür}

Es braucht nur wenige Forthworte, um einen Aufruf externer DLLs zu machen:

\begin{itemize}\setlength{\itemsep}{0pt}\setlength{\parskip}{0pt}
\item Debugging words: \texttt{.PROCS}, \texttt{.LIBS}
\item Defining words: \texttt{PROC}, \texttt{WINLIBRARY}
\item Calling words: \texttt{CALL}, \texttt{REL>ABS}, \texttt{ABS>REL}
\item Naming words: \texttt{AS}
\end{itemize}


\section{Schritt 1. Finde die Dokumentation der DLL}

Ohne solch ein Dokument ist es weder möglich, herauszufinden welche Namen die Funktionen einer DLL haben, noch welche Parameter übermittelt werden müssen, noch was zurückkommt. Wertvolle Dokumentationen zu den grundlegenden DLLs findet man bei msdn.microsoft.com, aber es gibt auch eine Menge weiterer Anbieter dafür. Für speziellere DLLs braucht man die von denen mitgelieferten Dokumente.  

\section{Schritt 2. Melde die DLL bei Win32Forth an}

Wird dem Win32Forth der Name einer DLL mitgeteilt, lädt es diese. Die Anweisung WINLIBRARY KERNEL32.DLL ermöglicht es dem W32F, diese DLL zu laden, damit dann deren Routinen benutzt werden können. Der Suchpfad für die DLLs variiert je nach Betriebssystem (95/98/ME/NT/2000/XP). Wird einfach nur der DLL-Name angegeben, muss die DLL im Systemverzeichnis liegen, oder in dem Verzeichnis, von dem aus W32F selbst aufgerufen worden ist. Die meisten DLLs werden auf diese Weise dann auch gefunden, und es wäre ungewöhnlich, wenn Pfadangaben gemacht werden müssten. WINLIBRARY kann auch mehrfach aufgerufen werden, es wird trotzdem immer nur eine einzige Kopie davon im Speicher sein. Mit .LIBS erfährt man, was schon alles geladen wurde:

\begin{verbatim}
Location Handle   Type Name
-------- -------- ---- -----------
0003EA5C -noload- APP  TESTIT.DLL
00014C08 10000000 APP  WINCON.DLL
. . .
\end{verbatim}

Dieses Wort wird gewöhnlich nur von der Kommandozeile einer Konsole aus benutzt. Im Beispiel sieht man, dass TESTIT.DLL zwar spezifiziert, aber noch nicht geladen wurde. Falls du Fehlermeldungen beim Aufruf von Routinen hast, die W32F eigentlich schon kennen sollte, schau bei der Adresse des \emph{handle} nach. Wird dort noch \verb|-noload-| angegeben, konnte W32F die Bibliothek für diese Routine nicht finden.

\section{Schritt 3.  Melde deren Prozeduren$^3$\\\hspace*{\fill}bei Win32Forth an}

Das Forthwort \texttt{PROC} wird verwendet, um die verschiedenen Prozeduren einer DLL (functions) vom W32F aus zu nutzen. In den Dokumenten der DLLs finden sich beispielsweise solche Angaben:

\subsection{AllocConsole}
Die Funktion \texttt{AllocConsole} startet eine neue Konsole für Programmaufrufe. (to allocate = Platz zuweisen)\\
\texttt{BOOL AllocConsole(void);}

\subsection{Parameter} 
Die Funktion nimmt keine Parameter an.

\subsection{Rückgabewerte}
Wenn die Funktion erfolgreich beendet wurde, ist der zurückgegebene Wert verschieden von null (nonzero). Sollte die Funktion hingegen scheitern, ist der Wert null.

Diese Funktion legt man im W32F nun so an: 

\texttt{0 PROC AllocConsole}

d.$\,$h., die Funktion nimmt 0 Parameter und heißt \verb|AllocConsole|. Beachte dabei unbedingt die Groß-- und Kleinschreibweise! Du MUSST die Namen so wie in deren Dokumention gezeigt eintippen. Obschon gerade in diesem Beispiel die Zahl nur symbolischen Wert hat und lediglich für die Dokumentation gedacht ist, denn ihr Wert kann 0 bis 127 sein, darf sie doch nicht negativ sein, weil damit spezielle Prozeduren des Systems gekennzeichnet werden. 
(Und wie schließt man die Konsole wieder? Lies aufmerksam weiter und du wirst es herausfinden.)

Dieser Schritt ist eigentlich optional (und aus didaktischen Gründen hier angeführt) --- man muss die PROC--Anweisungen eigentlich nicht ausdrücklich angeben, weil W32F während der CALL--Anweisung alle Prozeduren dynamisch selbst erzeugt. Die Anzahl der Prozeduren ist so aber auf die maximal dynamisch erzeugbaren begrenzt, das sind derzeit so um die 100 bis 150 Stück. Mit .PROCS sieht man welche Prozeduren schon erzeugt worden sind, und man kann die Ausgabe auch eingrenzen auf einen Namensteil (hierbei ist die Groß-- oder Kleinschreibung mal ohne Bedeutung): 

\begin{footnotesize}
\begin{verbatim}
.PROCS ALLOC
Location  ProcName          Prm  ProcEP    LibName
--------  --------          ---  --------  --------
0003EA5C  AllocConsole      0    
00012814  GlobalLock             77E977E4  KERNEL32.DLL
000127F4  GlobalAlloc            77E96646  KERNEL32.DLL
0000B830  HeapReAlloc       4
0000B7F8  HeapAlloc         3    77FCB10F  KERNEL32.DLL
Allocated Space:   12,288 bytes
Free Space Left:    6,997 bytes
Displayed 5 of 184 procedures defined
\end{verbatim}
\end{footnotesize}

\section{Schritt 4. Funktion aufrufen}

Da nun die DLL (library) angelegt ist, und optional auch deren Prozedur, können wir mit CALL AllocConsole ihre Funktionen ausführen --- nochmal: Die Schreibweise ist von Bedeutung und muss der Doku genau entsprechen! Nach diesem CALL sollte man eine frisch geöffnete DOS--Konsole sehen und eine 0 auf dem Stack vorfinden.



\section{Weitere Eigenschaften von DLLs}

\subsection{Die Abfolge der Parameter}

Forth übermittelt alle Parameter an die Windows--Funktionen mit dem Datenstack. Dort liegen sie in umgekehrter Abfolge. Also, wenn die Dokumentation fordert, dass \texttt{BOOL Function ( A, B, C )} anzugeben ist, schreiben wir in Forth  \texttt{C B A CALL Function}. Diese Umkehrung ist grundsätzlich nötig, um die Forth--Aufrufe mittels \texttt{CALL} laufen zu lassen.


\subsection{Prozeduren benennen}

Manche Funktionen im Windows haben bis zu drei Namen. Zum Beispiel gibt es da \texttt{CharUpperBuff}, \texttt{CharUpperBuffA} und \texttt{CharUpperBuffW} nebeneinander. Tatsächlich aber sind es nur zwei verschiedene Routinen. Schau dir die folgende Liste an, die mit \texttt{.PROCS} erstellt wurde$^4$: 

\begin{footnotesize}
\begin{verbatim}
Location  ProcName              Prm  ProcEP    LibName
--------  --------              ---  --------  --------
0003EA98  CharUpperBuffW        2    77E1236F  USER32.DLL
0003EA74  CharUpperBuffA        2    77E1263D  USER32.DLL
00003350  CharUpperBuff         2    77E1263D  USER32.DLL
\end{verbatim}
\end{footnotesize}

In der Spalte \texttt{ProcEP} steht die Startadresse (entrypoint) einer Prozedur. Wie man sieht, rufen \texttt{CharUpperBuff} und \texttt{CharUpperBuffA} dieselbe Funktion auf, nämlich die für ASCII--Zeichen genutzte, bei der jedes Zeichen durch 8 Bit dargestellt wird, also eine 1--Byte--Funktion. W hingegen ist die Funktion für den UNICODE, also die 2--Bytes--Funktion. 

Gewöhnlich wirst du die Ascii--Funktion benutzen, da W32F ein 1--Byte--pro--Zeichen--System ist. Von jeder Funktion, die sowohl in der A-- wie in der W--Variante vorkommt, lädt W32F automatisch nur die A--Version, ohne dass du das extra aussuchen musst. Falls du aber wirklich die W--Funktion haben willst, musst du sie ausdrücklich aufrufen. Also z.B. so: 

\texttt{2 PROC CharUpperBuffW}



\subsection{Die zurückgegebenen Werte (return values)}

Generell lässt jede Funktion immer nur einen Wert auf dem Stack zurück. Weil die meisten Funktionen in C oder C++ geschrieben sind, und daher multiple Werte gar nicht zurückgeben können, muss man andere Techniken benutzen, soll eine Funktion mehrere Werte zurückgeben. 

\texttt{BOOL} ist ein Beispiel für solch einen Rückgabewert. Dabei steht im Windows die 0 für \emph{wahr} (oder OK) und die 1 für \emph{falsch} (nicht--OK). Das ist anders herum als im Forth. Nimm \texttt{0=}, um \texttt{BOOL} auszuwerten und zum forthigen \texttt{TRUE} oder \texttt{FALSE} zu kommen:

\verb|CALL AllocConsole 0= IF ( alles ist OK ..)|

Oder du wertest das so aus:

\verb|CALL FreeConsole ABORT" FreeConsole failed"|

\texttt{INT}, \texttt{HANDLE}, usw. ergeben 32--Bit--Werte (unsigned or signed 32 bit) auf dem Stack. Verwende sie wie sonst im Forth auch. Auch \texttt{VOID} gibt einen 32--Bit--Wert auf den Stack, der hat aber keine Bedeutung (void = leer, Lücke) und kann einfach mittels \texttt{DROP} verworfen werden.



\subsection{Parameter von Funktionen interpretieren}

Leider kommen die Parameter von Funktionen in unterschiedlichen Geschmacksrichtungen daher. So muss neben deren Abfolge auch deren Datentyp  stimmen, damit der \texttt{CALL} funktioniert. Ein Beispiel: 

\begin{footnotesize}
\begin{verbatim}
BOOL ReadFile( HANDLE hFile, // handle to file LPVOID 
lpBuffer,                    // data buffer 
DWORD nNumberOfBytesToRead,  // number of bytes to read 
LPDWORD lpNumberOfBytesRead, // number of bytes read
LPOVERLAPPED lpOverlapped    // overlapped buffer 
   );
\end{verbatim}
\end{footnotesize}

wird im W32F zu:

\begin{footnotesize}
\begin{verbatim}
0 VALUE HANDLE
CREATE BUFFER 255 ALLOT 
20 VALUE BYTESTOREAD 
VARIABLE BYTESREAD 
: MYFUNC 
  0               \ null address for overlapped buffer 
  BYTESREAD       \ bytes read 
  BYTESTOREAD     \ bytes to read 
  BUFFER          \ address of buffer 
  HANDLE          \ value of handle 
  CALL ReadFile ;
\end{verbatim}
\end{footnotesize}

Die Parameter--Typen \texttt{HANDLE}, \texttt{BYTE}, \texttt{WORD}, \texttt{DWORD} liefern einfach einen Stackwert. Ihre Namen werden also interpretiert. Falls der Wert in \texttt{HANDLE} 13 wäre, käme die 13 auf den Stack.

Die \texttt{LP}xxx--Typen (ohne \texttt{STR}) hingegen sind Zeiger auf Datenbereiche. Es klappt also für gewöhnlich nicht, wenn man \texttt{VALUE} <name> benutzt, um \texttt{LP}xxx--Parameter zu definieren. Besonders wenn die Funktion einen Wert zurückliefert geht das schief, wie im Fall von \texttt{BYTESREAD}. Benutze lieber \texttt{VARIABLE}, weil dann die Adresse auf den Stack kommt. 

Und die \texttt{LP}x\texttt{STR}--Typen behandeln Zeichenketten, die mit einer Null enden (Null (0) terminated strings). Man benutzt sie z.~B.\ so:

\begin{verbatim}
: UPPERCASE ( caddr -- caddr ) 
    DUP COUNT SWAP 
    call CharUpperBuff DROP ;
\end{verbatim}

Es wird also die einfache Adresse einer Zeichenkette mit vorangehendem Zähler (counted string) in die Startadresse der Zeichen und die Länge der Kette umgewandelt, und deren Abfolge wird dann umgekehrt (len addr). Das ist nötig, weil der Windows--Funktionsaufruf zuerst den Zeiger auf die Zeichenkette und dann erst deren Länge erwartet. Zurück kommt dann wieder ein Wert, in diesem Fall ein n, an dem wir aber nicht weiter interessiert sind. Im Übrigen ist dieses Beispiel nicht einmal ein richtiger null--begrenzter Zeichenketten--Aufruf. Aber das hier ist einer:

\verb|int lstrlen(LPCTSTR lpString);|

Die Funktion zählt die Anzahl der Zeichen in der Kette. So ergibt
\verb|Z" ABCDEFG" CALL lstrlen|
eine 7 auf dem Stack, das ist die Länge der Zeichenkette. Folgende Zeichenketten--Typen des W32F sind null--begrenzt und sicher verwendbar auch in solchen Windows--Aufrufen:

\verb|C"| --- aber nur wenn der Zählwert berücksichtigt wird, geht es gut. Also  immer so verwenden:
\verb|C" ABCDEFG" COUNT DROP|
Nun steht die Adresse auf A, also auf dem Anfang der Zeichenkette, und nicht mehr auf dem Zähler. 

\verb|S" ABCDEFG"| legt (addr len) auf dem Stack ab. Hierbei zeigt die Adresse bereits richtig auf das A am Anfang der null--begrenzten Kette. Ob len verwendet wird, hängt ab von der Funktion, die folgen soll.

\verb|Z" ABCDEFG"| funktioniert wie \verb|C"|, aber mit dem Unterschied, dass gar keine Längenangabe erfolgt. Daher können \verb|Z"|--Ketten direkt benutzt werden für die Windows--Aufrufe.


Und hier noch eine Technik für den Fall, dass du deinen eigenen Puffer für eine null--begrenzte Zeichenkette erzeugen willst.

\begin{verbatim}
CREATE MYSTR 256 ALLOT  \ my string 
S" ABCDEGF" MYSTR PLACE \ move string to MYSTR 
MYSTR +NULL             \ add null on the end 
MYSTR CALL lstrlen \ use in call.
\end{verbatim}


\subsection{AS verwenden}

Gelegentlich ist es hilfreich, ein Forth--Wort zu haben, mit dem das XT einer Prozedur verwendet werden kann. Auch sind manche DLL--Namen recht lang und obskur ausgefallen, und eine bedeutsamere kürzere Benennung wäre wünschenswert. So fügt

\verb|1 PROC ExitThread AS EXIT-TASK|

das Wort \texttt{EXIT-TASK} an die aktuelle Wortliste an (current wordlist). AS muss nach der Deklaration einer Prozedur stehen, Kommentare dürfen dazwischen sein. 



\subsection{Gefährliche Tricks}

Nun folgt noch ein gefährlicher Trick. Nutze so etwas nur, wenn du DLL--Funktionen schon sicher aufrufen kannst. Nimm an, dass die Funktion \texttt{BOOL function(LPWORD param);} eine 10 auf die Adresse schreibt, die ihr übergeben worden ist. Zusätzlich gibt sie eine 1 zurück. Dann kann auch so etwas gemacht werden:  

\verb|0 SP@ ... CALL function|

Der Teil vor dem CALL manipuliert den Stack:\\
\verb!0 | addr of previous cell |!

Nach dem Funktionsaufruf hat der Stack dann die Werte\\
\verb!10 | 1!

Macht man es so, ist keine \texttt{VARIABLE} mehr nötig, um die 10 zu übergeben! Der ReadFile--Aufruf, den wir weiter oben schon mal ausprobiert hatten, kann dann auch so geschrieben werden:

\begin{footnotesize}
\begin{verbatim}
: MYFUNC 0        \ null address for overlapped buffer 
  0 SP@           \ bytes read = zukünftige 'Variable' 
  BYTESTOREAD     \ bytes to read 
  BUFFER          \ address of buffer 
  HANDLE          \ value of handle 
  CALL ReadFile ;
\end{verbatim}
\end{footnotesize}

Die Funktion liefert auf diese Weise dann 2 Werte direkt auf dem Stack ab, einmal die Anzahl Bytes, die gelesen wurden, und dann oben drauf noch den Rückgabewert. Sie hat also die von \texttt{sp@} ermittelte Adresse wie eine Variable benutzt, um darin die 10 zu übergeben. 

\section{Anmerkung}
Die Dokumentation so eines umfangreichen Systems wie das Win32Forth ist keine leichte Aufgabe, und die neuesten Änderungen am System werden dort nicht immer sofort eingearbeitet sein. Weil es eine Gruppenarbeit ist, war nicht immer so ganz klar, welchem Autor jeweils der Dank gebührt für einen bestimmten Beitrag zu der Dokumentation. Manches war auch von vornherein eine Gemeinschaftsarbeit. Namentlich genannte Mitarbeiter, denen ich an dieser Stelle meinen Dank und meine Anerkennung für ihre mühevolle Arbeit aussprechen möchte, sind (in alphabetischer Reihenfolge): Robert Dudley Ackerman, Ezra Boyce, Dirk Busch, Thomas Dixon, Brad Eckert, Bruno Gauthier, George Hubert, Alex McDonald, Rod Oakford, Andrew Stephenson, Jos van de Ven und noch andere.

Dirk Busch danke ich sowohl für die freundliche Hilfe bei der Durchsicht der Übersetzung als auch für die Hinweise auf die neuen Features des W32F 6.13. 



\section{Fußnoten}

(1) Win32Forth \emph{We}\/--Benutzergruppe:\\
\url{http://tech.groups.yahoo.com/group/win32forth/}

(2) Grundlagen der DLLs:\\
http://win32forth.sourceforge.net/doc/p-windlls.htm

(3) Im englischen Original werden die Begriffe procedure, function und routine synonym benutzt. In der Übersetzung werden daher ebenfalls  Prozedur, Funktion oder Routine als Begriffe für dieselbe Sache benutzt.

(4) Die Liste \texttt{CharUpperBuff} usw.\ hat erst dann Einträge in den Spalten \texttt{ProcEP} und \texttt{LibName}, wenn die Funktionen auch einmal gelaufen sind. Bis dahin bleiben diese Spalten leer. Um das Beispiel nachvollziehen zu können, überlege, wie du einen Aufruf der Funktionen machen könntest.
\end{multicols}

\section{Quellen}
\begin{enumerate}\setlength{\itemsep}{0pt}\setlength{\parskip}{0pt}
\item \emph{Win32Forth, Calling Windows Procedures, The Basics.},\\ 
Document 
\texttt{\$ Id: p-windlls.htm,v 1.1 2004/12/21 00:18:57 
alex\_mcdonald Exp \$}\\
auf: \url{http://win32forth.sourceforge.net/doc/p-index.htm}
\end{enumerate}

\end{document}
