TAPI-Telefonschnittstelle
Willemers Informatik-Ecke

Überblick und Hinweise

Die TAPI ist eine Schnittstelle von Microsoft, implementiert in den Windows-Versionen seit XP und teils schon früher.

Die TAPI 1.x ist in der Lage, ein beispielsweise über USB direkt am Computer angeschlossenes Telefon zu steuern.

Die TAPI 2.x kann eine Third Party Anbindung an die Telefonanlage über das Netzwerk steuern.

Für die TAPI 3.x gibt es mit JTAPI sogar eine Java-Anbindung. TAPI 3.x basiert auf COM. Leider wird es selten von den Telefonanlagenherstellern unterstützt.

Microsoft dokumentiert die TAPI in seiner MSDN: MSDN-Artikel TAPI 2.2 Overview

First Party wird die direkte Anbindung eines Computers an ein Telefon genannt. Der PC kann die Aktivitäten des Telefons übernehmen, etwa abheben, wählen oder weiterverbinden.

Daneben gibt es die Anbindung eines Computers an die Telefonanlage über das lokale Netzwerk, die als Third Party bezeichnet wird.

Einrichtung des TAPI-Clients

Um eine TAPI-Anwendung zu starten, muss der Rechner als TAPI-Client eingerichtet werden.

Zum Einrichten eines TAPI-Clients wird der Befehl tcmsetup aufgerufen, beispielsweise in der Ausführungszeile, die über die Tastenkombination [Windows]-[R] gestartet werden kann. Ansonsten wird es über die Eingabeaufforderung gestartet.

tcmsetup /c tapiserver
Als tapiserver wird der Name des TAPI-Servers genannt, der die Telefonanlage bedient.

Initialisierung

Als erste Funktion wird lineInitializeEx aufgerufen. Die Funktion liefert vor allem das Applikations-Handle (lphLineApp) und die Anzahl der zur Verfügung stehenden Geräte (lpdwNumDevs).

LONG WINAPI lineInitializeEx(
  LPHLINEAPP lphLineApp,
  HINSTANCE hInstance,
  LINECALLBACK lpfnCallback,
  LPCSTR lpszFriendlyAppName,
  LPDWORD lpdwNumDevs,
  LPDWORD lpdwAPIVersion,
  LPLINEINITIALIZEEXPARAMS lpLineInitializeExParams
);
lphLineApp
Zeiger auf eine Variable vom Typ HLINEAPP, die später das Handle der Applikation zum TAPI enthalten wird.
hInstance
Hier wird das Instanz-Handle der Anwendung oder der DLL übergeben. Der Parameter darf lt. Doku auch NULL sein.
lpfnCallback
Hier wird die Callback-Funktion angegeben, die aufgerufen wird, wenn die Anwendung als Ereignisrückmeldemethode "hidden window" verwendet. Bei den anderen Methoden wird dieser Eintrag ignoriert und sollte NULL sein.
lpszFriendlyAppName
Ein Text, der den Namen der Anwendung enthält. Dieser Paramter dient hauptsächlich dekorativen Zwecken und kann auch NULL gesetzt werden.
lpdwNumDevs
Hier wird die Adresse der DWORD-Variablen abgelegt, in der nach einem erfolgreichen Aufruf die Anzahl der verfügbaren Line-Geräte stehen soll.
lpdwAPIVersion
Hier wird die Adresse einer DWORD-Variablen übergeben, die die TAPI-Version enthält. Vor dem Aufruf wird dieser Wert mit dem höchsten Wert belegt, den die Anwendung unterstützt.
lpLineInitializeExParams
Adresse einer LINEINITIALIZEEXPARAMS-Struktur. Das Element dwTotalSize muss vor dem Aufruf unbedingt korrekt gesetzt sein. Mit dwOptions wird die Ereignis-Rückmeldung bestimmt. Siehe weiter unten.
Rückgabewert:
0, wenn der Aufruf erfolgreich war, ansonsten eine negative Fehlernummer.

Anmeldung der Ereignisrückmeldung

Durch Besetzung des Feldes dwOption in der Struktur LINEINITIALIZEEXPARAMS, die als Parameter an die Funktion lineInitializeEx übergeben wird, wird festgelegt, wie die Ereignisse der Telefonanlage an die Anwendung zurückgemeldet werden.
hidden window
dwOption=LINEINITIALIZEEXOPTION_USEHIDDENWINDOW;
event handle
dwOption=LINEINITIALIZEEXOPTION_USEEVENT;
Der Aufruf liefert im Element hEvent das Event-Handle, das aber nicht weiterverwendet werden muss. Man kann es WaitForSingleObject verwenden. Diese Variante der Ereignissuche wird hier weiter verfolgt.
completion port
dwOption=LINEINITIALIZEEXOPTION_USECOMPLETIONPORT;
Das Element hCompletionPort muss mit dem Handle besetzt werden, das mit der Funktion CreateIoCompletionPort verwendet wird.

Versioning

Man fragt die eigene Schnittstelle und die Gegenstation, welche TAPI-Version sie beherrscht. Die Funktionen lauten lineNegotiateAPIVersion. Für Erweiterungen der TAPI gibt es noch die Funktion lineNegotiateExtVersion.

Die Funktion wird für jede Leitung aufgerufen. Der Wert von dwDeviceID liegt zwischen 0 und der Anzahl der gemeldeten Leitungen/Geräte -1.

LONG WINAPI lineNegotiateAPIVersion(
  HLINEAPP hLineApp,
  DWORD dwDeviceID,
  DWORD dwAPILowVersion,
  DWORD dwAPIHighVersion,
  LPDWORD lpdwAPIVersion,
  LPLINEEXTENSIONID lpExtensionID
);
Die Parameter haben folgende Bedeutung
hLineApp
Das TAPI-Handle.
dwDeviceID
Index der Leitung zwischen 0 und Gerätezahl-1.
dwAPILowVersion
Die niedrigste TAPI-Version, mit der die Anwendung arbeiten kann oder will.
dwAPIHighVersion
Die höchste TAPI-Version, die die Anwendung beherrscht.
lpdwAPIVersion
Hier wird die Adresse eine DWORD-Variable angegeben, in der die Funktion die TAPI-Version ablegt, die das Gerät beherrscht. Diese muss bei den Aufrufen fuer diese Leitung übergeben werden.
lpExtensionID
Zeiger auf eine Variable vom Typ LINEEXTENSIONID. Nur wichtig, wenn der Geräteanbieter irgendwelche Erweiterungen zur TAPI implementiert.
Rückgabewert
0 für Erfolg, ansonsten eine negative Fehlernummer.

Leitungen und Geräte: lineGetDevCaps

lineGetDevCaps beschafft die Informationen über die verfügbaren Leitungen. Die Eingabeparameter sind das TAPI-Instance-Handle der Anwendung, die Gerätenummer und die TAPI-Version. Zurückgeliefert wird ein Zeiger auf eine Struktur vom Typ LINEDEVCAPS. Aber Vorsicht! Die Größe ist vor dem Aufruf nicht bekannt.

Hintergrund ist die MSDN-Information von Microsoft.

LONG WINAPI lineGetDevCaps(
  HLINEAPP hLineApp,
  DWORD dwDeviceID,
  DWORD dwAPIVersion,
  DWORD dwExtVersion,
  LPLINEDEVCAPS lpLineDevCaps
);
hLineApp
Das Applikationshandle der TAPI, wie sie von lineInitializeEx geliefert wird.
dwDeviceID
Die Nummer der Leitung. Die Leitungen werden durchnummeriert von 0 bis zur maximalen Zahl der Geräte, die als lpdwNumDevs von der Funktion lineInitializeEx geliefert wird.
dwAPIVersion
Die Versionsnummer der TAPI kann für jedes Gerät anders sein. Am besten, man bestimmt diese zuvor über die Funktion lineNegotiateAPIVersion.
dwExtVersion
Versionsnummer für Erweiterungen des Herstellers. In den meisten Fällen ist 0 eine gute Idee.
lpLineDevCaps
Ein Zeiger auf eine Struktur LINEDEVCAPS. Vor dem Aufruf muss das Element dwTotalSize auf die Größe der Struktur festgesetzt werden, sonst kommt es zu unvorhersehbaren Effekten. Siehe unten!
Rückgabewert
Die Funktion liefert 0, wenn alles klappte. Ansonsten eine negative Fehlernummer.

Die LINEDEVCAPS-Struktur

Die ausführliche Strukturbeschreibung findet sich auf den MSDN-Seiten.

Die TAPI versucht, an die LINEDEVCAPS-Struktur noch einige Zeichenketten anzuhängen, die dann per Offset erreicht werden. Darum muss zwischen Anwendung und TAPI ausgehandelt werden, wie groß der zur Verfügung stehende Speicher ist. Drei Elementvariablen sind für diese Aushandlung relevant.

dwTotalSize
Dies ist die Größe des Speichers, den die Anwendung zur Verfügung stellt. Sie sollte mindestens sizeof(LINEDEVCAPS) sein. Man kann auch in weiser Voraussicht ein wenig mehr spendieren.
dwNeededSize
Die TAPI besetzt anschließend dieses Feld mit der Größe, die sie benötigt. Ist der Wert höher als dwTotalSize, sollte die Anwendung neuen Speicher anfordern und den Aufruf wiederholen.
dwUsedSize
Enthält die Größe des Speicherbereichs, der tatsächlich mit Daten gefüllt wurde.
Als Beispiel für Daten die an der LINEDEVCAPS-Struktur hängen, verwenden wir hier den Leitungsnamen. Zwei Elemente sind dafür vorhanden.
dwLineNameSize
Die Länge des Leitungsnamens. Ist dies 0, gibt es keinen.
dwLineNameOffset
Dieser Wert gibt die Distanz zwischen dem Anfang der LINEDEVCAPS-Struktur und dem Anfang des Leitungsnamens an. Soll der Leitungsname in einen String namens ziel kopiert werden, gibt man folgende Anweisung:

int lineDevCapsSize = sizeof(LINEDEVCAPS + 4096;
LINEDEVCAPS *lineDevCaps = new char[lineDevCapsSize];
lineDevCaps->dwTotalSize = lineDevCapsSize;
long err = lineGetDevCaps( ..., lineDevCaps);
...
memcpy(ziel, (char*)lineDevCaps + lineDevCaps->dwLineNameOffset, lineDevCaps->dwLineNameSize);

Die Telefonnummer einer Leitung: lineGetAddressCaps

Diese Funktion ermittelt die Telefonnummer der angegebenen Leitung. Die Informationen basieren auf der MSDN-Seite zu lineGetAddressCaps.
LONG WINAPI lineGetAddressCaps(
  HLINEAPP hLineApp,
  DWORD dwDeviceID,
  DWORD dwAddressID,
  DWORD dwAPIVersion,
  DWORD dwExtVersion,
  LPLINEADDRESSCAPS lpAddressCaps
);
hLineApp
Anwendungs-TAPI-Handle
dwDeviceID
Die Nummer der Leitung. Die Leitungen werden durchnummeriert von 0 bis zur maximalen Zahl der Geräte, die als lpdwNumDevs von der Funktion lineInitializeEx geliefert wird.
dwAddressID
Die ID der Adresse. Hier funktioniert 0.
dwAPIVersion
Hier wird die TAPI-Version übergeben. Die Versionsnummer erhält man durch Aufruf von lineNegotiateAPIVersion.
dwExtVersion
Hier funktioniert auch 0.
lpAddressCaps
Hier wird ein Zeiger auf eine Datenstruktur vom Typ LINEADDRESSCAPS übergeben. Die Daten muss der Aufrufer angefordert haben - und ein wenig mehr. Die Größe der Datenstruktur muss in dem Element dwTotalSize vor dem Aufruf abgelegt werden.
Rückgabewert
Liefert bei Erfolg eine 0 zurück, ansonsten eine Fehlernummer.

Registrieren einer Leitung für den Ereignisempfang: lineOpen

lineOpen meldet eine Leitung an, deren Ereignisse gefangen werden soll. Der Aufruf ist auch die Voraussetzung dafür, Anrufe mit den Funktionen lineMakeCall, lineUnpark, linePickup, lineSetupConference oder lineForward zu steuern.

Die Informationen dieses Abschnitts basieren auf der MSDN-Seite zu lineOpen und eigenen Experimenten.

LONG WINAPI lineOpen(
  HLINEAPP hLineApp,
  DWORD dwDeviceID,
  LPHLINE lphLine,
  DWORD dwAPIVersion,
  DWORD dwExtVersion,
  DWORD_PTR dwCallbackInstance,
  DWORD dwPrivileges,
  DWORD dwMediaModes,
  LPLINECALLPARAMS const lpCallParams
);
hLineApp
Anwendungs-TAPI-Handle
dwDeviceID
Die Leitungsnummer, die geöffnet werden soll.
lphLine
Zeiger auf eine Variable, die nach dem Aufruf das Leitungs-Handle enthalten wird.
dwAPIVersion
Die TAPI-Version, auf die sich Anwendung und Gerät/Leitung mittels der Funktion lineNegotiateAPIVersion geeinigt haben.
dwExtVersion
Die Version der Hersteller-Erweiterung, sofern eine genutzt wird. Normalerweise wird hier 0 übergeben.
dwCallbackInstance
Hier kann die Applikation einen Zeiger auf einen beliebigen Datenbestand übergeben, der beim Eintreffen eines TAPI-Ereignisses zurückgegeben wird. Die TAPI selbst interpretiert diesen Parameter nicht.
dwPrivileges
Hier werden Privilegien gesetzt, die die Applikation anfordert. Typischerweise sind dies LINECALLPRIVILEGE_MONITOR | LINECALLPRIVILEGE_OWNER.
dwMediaModes
Hier werden die LINEMEDIAMODE_-Konstanten gesetzt, typischerweise LINEMEDIAMODE_VOICEVIEW.
lpCallParams
Dieser Wert wird ignoriert, wenn nicht LINEMAPPER or LINEOPENOPTION_PROXY benutzt werden, und darum auf 0 gesetzt.
Rückgabewert
Die Funktion gibt 0 im Erfolgsfall, andernfalls eine negative Fehlernummer zurück.
LINEERR_ALLOCATED
Die Leitung ist permanent durch einen anderen Prozess belegt.
LINEERR_RESOURCEUNAVAIL
Die Leitung ist kurzfristig belegt, kann aber vielleicht etwas später wieder verfügbar sein. Ein neuer Versuch ist sinnvoll.
LINEERR_REINIT
Durch eine TAPI-Reinitialisierung ausgelöst. Die Anwendung muss wohl neu initialisieren.
Durch openLine kann eine Applikation über diese Leitung Anrufe auslösen. Ob die Anwendung Anrufe handhaben kann, hängt von dem Parameter dwMediaModes ab. Will die Anwendung Anrufe nur beobachten, spezifiziert sie LINECALLPRIVILEGE_MONITOR. Will Sie nur selbst anrufen, reicht LINECALLPRIVILEGE_NONE. Spezifiert die Anwendung LINECALLPRIVILEGE_OWNER und LINEMEDIAMODE_UNKNOWN, übernimmt sie auch die Kontrolle über unklassifizierte Anrufe.

Beim Aufruf von openLine ist die API-Version von entscheidender Wichtigkeit. Darum sollte diese zuvor unbedingt per lineNegotiateAPIVersion ermittelt werden.

Ereignisse auslesen: lineGetMessage

Die Funktion lineGetMessage fragt die Ereignisse aller durch lineOpen geöffneten Leitungen an.
LINEMESSAGE lineMessage;   
long err = lineGetMessage(handleLineApp, &lineMessage, 0);
Der Prototyp der Funktion lautet:
LONG WINAPI lineGetMessage(
  HLINEAPP hLineApp,
  LPLINEMESSAGE lpMessage,
  DWORD dwTimeout
);
hLineApp
Das Handle der Anwendung
lpMessage
Zeiger auf eine Struktur des Typs LINEMESSAGE, in der die Funktion ihre Ergebnisse hinterlegt.
dwTimeout
Timeout in Millisekunden. Wie lange die Funktion auf Ereignisse wartet.
Rückgabe
0 bei Erfolg, negativ bei Fehler. Dabei bedeutet der Fehler LINEERR_OPERATIONFAILED lediglich, dass noch keine Ereignisse vorliegen.
Es wird empfohlen, nach dem erfolgreichen Empfang eines Ereignisses so lange zu wiederholen, bis kein Ereignis mehr vorhanden ist.

Die LINEMESSAGE-Struktur:

typedef struct linemessage_tag {
  DWORD     hDevice;
  DWORD     dwMessageID;
  DWORD_PTR dwCallbackInstance;
  DWORD_PTR dwParam1;
  DWORD_PTR dwParam2;
  DWORD_PTR dwParam3;
} LINEMESSAGE, *LPLINEMESSAGE;
hDevice
Handle (nicht Index) der Leitung oder des Calls, je nachdem, was das Ereignis ausgelöst hat.
dwMessageID
kann folgende Werte annehmen:
LINE_REPLY
Wird nach einem lineMakeCall oder lineDrop ausgelöst. Der Aufruf dieser Funktionen erfolgt asynchron. Der Rückgabewert ist eine ID, kein Handle. Diese ID wird mit dem Parameter dwParam1 geliefert und identifiziert den Aufruf. dwParam2 liefert ggf. eine negative Fehlernummer, sonst 0. Erst nach LINE_REPLY ist auch das Call-Handle von lineMakeCall gültig.
LINE_CALLSTATE
Ein Anruf hat seinen Status verändert. Es wird weiter unterschieden anhand des Wertes in lineMessage.dwParam1:
LINECALLSTATE_OFFERING
Ein Anruf ist eingetroffen. Das Telefon klingelt.
LINECALLSTATE_IDLE
Der Anruf wurde beendet.
LINECALLSTATE_CONNECTED
Ein Anruf wurde verbunden.
LINECALLSTATE_DISCONNECTED
Ein Anruf wurde von der Gegenstation getrennt. Hier kann lineDrop aufgerufen werden.
LINE_CALLINFO
Ein Anruf liefert Informationen. Nach dem Aufruf von lineGetCallInfo lassen sich diese Informationen aus der Struktur LINECALLINFO auslesen. Welcher Bestandteil der Struktur verändert wurde, lässt sich anhand des Message-Parameters dwParam1 auslesen. Er enth&aulm;lt den eine Konstante vom Typ LINECALLINFOSTATE_xxx.
LINECALLINFOSTATE_CALLEDID
Die Telefonnummer, die der Anwender gewählt wurde, wurde eingetragen.
dwCallbackInstance
Hier wird der Zeiger wieder hervorgebracht, den die Anwendung beim Aufruf von lineInitializeEx im Parameter dwCallBackInstance abgelegt hat. Bei geschickter Wahl des übergebenen Zeigers kann die Ereignisbearbeitung die Leitung damit leicht identifizieren.
dwParam1, dwParam2 und dwParam3
Weitere Parmeter für die verschiedenen Nachrichtentypen.

Informationen über einen Anruf: lineGetCallInfo

lineGetCallInfo ermittelt die Daten über einen Anruf. Insbesondere die Telefonnummer des Anrufenden lässt sich hier ermitteln.
LONG WINAPI lineGetCallInfo(
  HCALL hCall,
  LPLINECALLINFO lpCallInfo
);
hCall
Handle für den Anruf
lpCallInfo
Zeiger auf eine Struktur LINECALLINFO
Rückgabewert
0 bei Erfolg, negativ als Fehlermeldung
Die LINECALLINFO-Struktur wird auf den MSDN-Seite ausführlich dokumentiert. Ein paar Highlights:

dwTotalSize, dwNeededSize, dwUsedSize
Das Element dwTotalSize muss vor dem Aufruf gesetzt sein. Es reicht nicht aus, nur die sizeof(LINECALLINFO) zu verwenden, sondern es muss etwas mehr reserviert werden, damit die TAPI noch Strings anhängen kann. Sollte der Platz nicht reichen, gibt es einen negativen Rückgabewert. Ansonsten liefert dwUsedSize, wieviel Speicher in Anspruch genommen wurde.
dwCallerIDSize, dwCallerIDOffset
Dahinter verbirgt sich die Telefonnummer des Anrufers als C-String. Er beginnt am Anfang der Struktur LINECALLINFO plus dwCallerIDOffset und ist dwCallerIDSize lang.

Die TAPI wird aktiv

Das Programm telefoniert: lineMakeCall

Alternativ - oder auch in Kombination - kann lineMakeCall zu lineDialverwendet werden. Hier wird die Leitungsnummer als Parameter übergeben werden. Die Leitung muss durch lineOpen zuvor geöffnet worden sein.

LONG WINAPI lineMakeCall(
  HLINE hLine,
  LPHCALL lphCall,
  LPCSTR lpszDestAddress,
  DWORD dwCountryCode,
  LPLINECALLPARAMS const lpCallParams
);
hLine
Das Handle der Leitung, über den die Wahl erfolgen soll.
lphCall
Der Aufruf liefert das Handle des Anrufs in eine Variable zurück, deren Adresse hier übergeben wird. Allerdings ist der Wert nicht sofort gültig, sondern erst, wenn ein LINE_REPLY-Ereignis durch lineGetMessage empfangen wurde.
lpszDestAddress
Die Telefonnummer als String, die angerufen werden soll.
dwCountryCode
Der Landes- oder Regionalcode der Zielnummer. Wird hier 0 übergeben, wird die Vorgabe der Telefonanlage verwendet.
Rückgabewert
Ist negativ, wenn ein Fehler auftrat, positiv bei asynchronem Ende der Funktion. Dieser Wert ist eine ID, die bei dem beim zugehörigen LINE_REPLY-Ereignis im Parameter dwParam1 steht.
Durch den Aufruf wird eine LINE_REPLY-Message ausgelöst. Deren Parameter dwParam2 ist 0, wenn der Aufruf erfolgreich war.

Das Programm wählt: lineDial

Mit der Funktion lineDial kann eine Nummer gewählt werden. Voraussetzung ist ein bereits existierender Anruf. Dieser könnte beispielsweise durch lineMakeCall erzeugt worden sein, wenn dieser keine Rufnummer als Paramter übergibt.

LONG WINAPI lineDial(
  HCALL hCall,
  LPCSTR lpszDestAddress,
  DWORD dwCountryCode
);
hCall
Das Handle des Anrufs über den die Wahl erfolgen soll. Dieses wird beispielsweise durch lineMakeCall erzeugt.
lpszDestAddress
Die Telefonnummer als String, die angerufen werden soll.
dwCountryCode
Der Landes- oder Regionalcode der Zielnummer. Wird hier 0 übergeben, wird die Vorgabe der Telefonanlage verwendet.
Rückgabewert
Ist negativ, wenn ein Fehler auftrat, positiv bei asynchronem Ende der Funktion.
Durch den Aufruf wird eine LINE_REPLY-Message ausgelöst. Deren Parameter dwParam2 ist 0, wenn der Aufruf erfolgreich war.

Das Programm legt auf: lineDrop

Mit der Funktion lineDrop legt das Programm den Hörer auf. Voraussetzung ist ein Call-Handle, also ein Anruf. Dieser könnte beispielsweise durch lineMakeCall erzeugt worden sein, oder bei einigen Ereignissen durch lineGetMessage ermittelt worden sein. Paramter übergibt.

LONG WINAPI lineDrop(
  HCALL hCall,
  LPCSTR lpsUserUserInfo,
  DWORD dwSize
);
hCall
Das Handle des Anrufs der beendet werden soll.
lpsUserUserInfo
Zeiger eines Strings, der an die andere Seite gesandt werden soll. Normalerweise NULL.
dwSize
Die Größe des Strings. Normalerweise 0.
Rückgabewert
Ist negativ, wenn ein Fehler auftrat, eine positive ID bei asynchronem Ende der Funktion. Diese ID wird beim LINE_REPLY-Ereignis in dwParam1 zur Identifikation gesendet.
Durch den Aufruf wird eine LINE_REPLY-Message ausgelöst. Deren Parameter dwParam2 ist 0, wenn der Aufruf erfolgreich war.

Das Programm hebt ab: lineAnswer

Die Funktion lineAnswer nimmt den Hörer ab, wenn ein Anruf anliegt. Dies erfährt das Programm durch ein Ereignis, das die Funktion lineGetMessage ausliest.

LONG WINAPI lineAnswer(
  HCALL hCall,
  LPCSTR lpsUserUserInfo,
  DWORD dwSize
);
hCall
Das Handle des Anrufs der entgegen genommen werden soll.
lpsUserUserInfo
Zeiger eines Strings, der an die andere Seite gesandt werden soll. Normalerweise NULL.
dwSize
Die Größe des Strings. Normalerweise 0.
Rückgabewert
Ist negativ, wenn ein Fehler auftrat, eine positive ID bei asynchronem Ende der Funktion. Diese ID wird beim LINE_REPLY-Ereignis in dwParam1 zur Identifikation gesendet.
Durch den Aufruf wird eine LINE_REPLY-Message ausgelöst. Deren Parameter dwParam2 ist 0, wenn der Aufruf erfolgreich war.