| Nachrichtenaustausch zwischen CAPI und Anwendung |
Funktionen wie die im letzten Kapitel angesprochene Operation CAPI_GET_PROFILE sind reine Informationsfunktionen. Die Funktion wird von der Anwendung aufgerufen und die CAPI liefert irgendwas zurück. Das wars dann auch. Darin liegt aber noch nicht der Kern der Kommunikation mit der CAPI, nämlich dem, worum es in 95% der CAPI Doku. geht. Die CAPI ist vor allem dazu da, den Datenaustausch zwischen ISDN-Anwendungen auf verschiedenen Rechnern über das ISDN-Netz zu ermöglichen. Das zu unterstützen beschreibt die CAPI Doku. einen ausgefeilten Kommunikationsmechanismus zwischen der CAPI und der ISDN-Anwendung.
Ablauf der Kommunikation im Überblick
Ein solcher Kommunikationsmechanismus besteht immer aus folgenden Aktionen:
Mindestens eine Frage bleibt noch: Mit CAPI_GET_MESSAGE holt die Anwendung eine Nachricht von der CAPI ab. Wie aber weiß die Anwendung, wann eine neue Nachricht zum Abholen bereitsteht?
Es ist zwar möglich, in einer Schleife CAPI_GET_MESSAGE solange aufzurufen, bis die Funktion keinen Fehler mehr liefert (sog. Polling-Verfahren), besonders ressourcensparend ist das aber nicht. Speziell für diesen Zweck gibt es nämlich die beiden Funktionen CAPI_SET_SIGNAL und CAPI_WAIT_FOR_SIGNAL. Welche dieser beiden Funktionen zu verwenden sind, hängt vom Betriebssystem ab. Über CAPI_SET_SIGNAL wird der CAPI die Adresse einer Callback-Funktion mitgegeben, die die CAPI aufruft, sobald eine neue Nachricht verfügbar ist. Diese Message ist für Betriebssysteme und Programme gedacht, die kein Multithreading unterstützten.
Für Windows 95 oder Windows NT gibt es bei Verwendung der CAPI2032.DLL die Funktion CAPI_WAIT_FOR_SIGNAL. Diese Funktion wird von der Anwendung in einem parallelen Thread aufgerufen (siehe CPHAND.PAS TCapiCallback). Die Programmausführung bleibt innerhalb des Threads solange in der Funktion CAPI_WAIT_FOR_SIGNAL stehen, bis eine neue Nachricht von der CAPI verfügbar ist oder die Anwendung sich von der CAPI abmeldet (siehe TCapiCallback.Execute).
Was ist eigentlich eine Nachricht ?
Einfach gesagt ist eine Nachricht ein Bereich im Arbeitsspeicher, der je nach dem Sinn der Nachricht bestimmte Inhalte enthalten muss. Will die Anwendung eine solche Nachricht an die CAPI geben, übergibt sie der Funktion CAPI_PUT_MESSAGE einen Zeiger auf den vorher aufbereiteten Speicherbereich. Bei CAPI_GET_MESSAGE erhält die Anwendung einen Zeiger auf einen von der CAPI aufbereiteten Speicherbereich.
Der allgemeine Aufbau einer solchen Nachricht ist immer gleich (CAPI Doku.Kapitel 3.3 Message Structure) . Der Speicherbereich beginnt mit einem Message-Header fester Länge, auf den ein Datenbereich variabler Länge folgt.
Message Header
Wie der Message-Header definiert ist, finden Sie in der Unit CPMSG.PAS als Typ TCapiMessageHeader. Die im public-Bereich definierten Variablen sind die Inhalte des Message-Headers.
| TotalLength | ist die Gesamtlänge der Nachricht (einschließlich Header). Diese Information ganz am Anfang der Nachricht ist notwendig, da bei CAPI_PUT_MESSAGE und CAPI_GET_MESSAGE ja jeweils nur ein Zeiger auf einen Speicherbereich übergeben wird und aus dem Zeiger allein die Länge der Nachricht nicht erkannt werden kann. Von einer maximalen Länge einer Nachricht, die es noch in der CAPI 1.1 mit 180 Bytes gab, ist in der CAPI 2.0 nicht mehr die Rede. |
| ApplId | ist die Kennung, die bei CAPI_REGISTER von der CAPI zugeteilt wurde. |
| Command, SubCommand | Die Felder Command und SubCommand enthalten den Typ der Message. SubCommand gibt an, ob es eine REQ, CONF, IND oder RESP-Message ist. Command identifiziert die Nachricht genauer. Bei der Beschreibung der Messages (siehe CAPI Doku. Kapitel 5) steht zu jeder Message als erstes, welches Command/Subcommand-Pärchen die Nachricht identifziert. Sie finden die in der CAPI Doku. definierten Werte in der Unit CPCONST.PAS als Konstantendefinitionen wieder. (z.B. LISTEN_REQ = $0580). |
| Messagnumber | Jede Nachricht bekommt eine Nummer zugeteilt, die z.B. der Kontrolle der Lückenlosigkeit der Nachricht dienen kann. Sendet die Anwendung einen REQ an die CAPI, setzt die CAPI beim folgenden CONF die selbe MessageNumber, die die Anwendung im REQ verwendet hat. Umgekehrt muss die Anwendung beim RESP dieselbe Nummer verwenden, die die CAPI beim zugehörigen IND verwendet hat. |
Message Parameter
Welche Daten auf den Message-Header folgen (dürfen), ist für die einzelnen Messages in der CAPI Doku. festgelegt. Eine sehr einfache Message INFO_RESP, die die Anwendung auf eine INFO_IND-Message der CAPI sendet, ist z.B. so definiert (siehe CAPI DOku. Kap. 5.36):
| INFO_RESP | Command | 0x08 |
| Subcommand | 0x83 |
| Parameter | Type | Comment |
| Controller/PLCI | dword | Controller / Physical Link Connection Identifier (as in INFO_IND) |
Im Speicher würde das dann etwa so aussehen:

Wichtig sind hier die fett markierten Werte in der Struktur. Totallength (12) beschreibt die Länge in Bytes (ein Kästchen sind 2 Bytes oder 1 word). Command/SubCommand ist der Eintrag für den Messagetyp: Command $08 für eine INFO-Message und Subcommand $83 für eine Message vom Typ RESP. ApplID, Messagenumber und PLCI wurden von der CAPI vergeben.
Parameter Typen
In der CAPI Doku. werden für Message-Parameter lediglich 4 Typen zugelassen, die sich, bis auf einen recht gut mit Hilfe von Object Pascal darstellen lassen:
| CAPI-Typ | Pascal-Typ |
| byte | byte |
| word | word |
| dword | dword (unit SysUtils) |
| struct | - |
Sucht man nach einem passenden Typ, um eine Struct darzustellen, drängt sich der alternde Pascal-String auf, dessen ersten Byte das Längenbyte ist, dem die Daten folgen. Eine CAPI-Struct hat aber die unangenehme Eigenschaft, dass sie auch länger als 255 Zeichen sein kann. In diesem Fall wird das erste Byte der Struct mit 255 belegt und erst das folgende word gibt die Länge der Struktur an. Eine genaue Entsprechnung des CAPI-Typs Struct gibt es also in Pascal nicht. Auch anderen Programmiersprachen geht es ähnlich, so dass beim Typ Struct immer Handarbeit angesagt ist.
Um eine Verwendung ähnlich eines Typs zu ermöglichen, wurde im Programmbeispiel die Definition als Objekt gewählt (TStruct in CPSTRUCT.PAS). Das nächste Kapitel erklärt ein wenig genauer, wie das Beispielprojekt funktioniert.