Entwickler-Ecke

Windows API - Pointer und kein Ende bei der TAPI


Peter18 - Di 16.06.15 11:52
Titel: Pointer und kein Ende bei der TAPI
Ein freundliches Hallo an alle,

immer Ärger mit den Pointern. :cry: Ich dachte die Tapi ist abgehakt, aber ein typischer Fall von denkste!

In diesem Fall soll ein Anrufmonitor den Anrufer zeigen. Aber mein Compiler scheint die Schnittstelle der Callback-Routine nicht korrekt zu erstellen. Ein bis zwei Anrufe werden signalisiert, dann ist Feierabend. Da auch ein Anrufmonitor aus den Internet nichts mehr sagt, gehe ich davon aus, das die TAPI zersägt worden ist. Nach Neustart geht es dann manchmal wieder, doch irgend wann ist auch das vorbei und es hilft nur die TAPI neu zu installieren.

Das steht in der Tapi.pas:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
    TTAPICallback = procedure(hDevice, dwMessage: DWORD; dwInstance, dwParam1,
    dwParam2, dwParam3: DWORD_PTR) stdcall;
//  +++++++++++++++ !! +++++++++++++++++++++++++++++++++++++++++++++
  {TLineCallback = procedure(hDevice, dwMessage: DWORD; dwInstance, dwParam1,
    dwParam2, dwParam3: DWORD_PTR) stdcall;}

  TLineCallback = TTAPICallback;
  LINECALLBACK = TLineCallback;
  {$EXTERNALSYM LINECALLBACK}


Das steht in meiner Unit:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
....
Type
  Ba  = Array [0..500of Byte;
  PBa = ^Ba;

procedure LineCallBack( hDev, Msg, Cbi, P1, P2, P3 : DWord );    stdcall;
....

Das Array dient zum kopieren der Rufnummern. Wenn ich die Type-Deklaration von dort unter "implementation" verschiebe, ändert sich die Fehlermeldung. Setze ich sie erneut an diese Stelle ändert sie sich jedoch nicht!

In dieser Zeile wird die "LineCallBack" angemeckert und das mit den unterschiedlichsten Fehlermeldungen.

Delphi-Quelltext
1:
R  := lineInitialize( @Luh, HInstance, LineCallBack, PChar( 'Test' ), DC );                    

Mal behauptet er die "LineCallBack" wäre nicht global deklariert, mal erwartet er ein "(" und findet ",". Mal geht es nur mit "@LineCallBack," und mal mit "LineCallBack,". Das Merkwürdige ist, mache ich die Änderung rückgängig (Auskommentieren) erscheint nicht immer der selbe Fehler wie zuvor.

Bei "procedure T_Tapi.WriteCallMon( B : Boolean );" wird nicht gemeckert, aber bei "procedure Dial( Nr: String; V : DWord; GeraetInd : DWord; Modem : Boolean );".

Bei dem Ganzen habe ich etwas die Peilung verloren, vielleicht kann mich jemand wieder auf Kurs bringen. Ist es vielleicht sinnvoll die CallBack in eine extra Unit zu stecken? Oder die CallBack einer Pointervariablen zuweisen? Oder kann ich die Deklaration in der Tapi.pas nutzen?

Ich hoffe auf einen guten Tipp und grüße von der sonnigen Nordsee

Peter


uall@ogc - Mi 17.06.15 13:22

Deine LineCallback Funktion sollte so aussehen (analog zur TTAPICallback)


Delphi-Quelltext
1:
procedure myLineCallback(hDevice, dwMessage: DWORD; dwInstance, dwParam1, dwParam2, dwParam3: DWORD_PTR) stdcall;                    


und beim Aufruf solltest du @LineCallback uebergeben. Das geht auch nur wenn die Funktion keine Methode ist.


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
unit bla;

interface

uses windows, usw;

procedure myLineCallback(hDevice, dwMessage: DWORD; dwInstance, dwParam1, dwParam2, dwParam3: DWORD_PTR) stdcall;

implementation

procedure myLineCallback(hDevice, dwMessage: DWORD; dwInstance, dwParam1, dwParam2, dwParam3: DWORD_PTR) stdcall;
begin
  // was machen
end;

// (die Funktion muss in keine Extra Unit ausgelagert werden, hier ist das nur als Beispiel, wenn du extern darauf zugreifen willst)


Ausserdem solltest dir auch mal lineInitializeEx anschauen


Achtung: eventl ist auch deine TTAPICallback falsch (DWORD_PTR wird in PDWORD sein) das ist dann nicht korrekt

ggf. hilft dir das:
http://gedemin.googlecode.com/svn/trunk/Comp5/tapi.pas


Edit:
deine TTAPICallback ist falsch deklariert, https://msdn.microsoft.com/en-us/library/ms735512%28v=vs.85%29.aspx
also stimmt mein Code oben nicht, du musst aber trotzdem @myLineCallback verwenden (und die Funktion sollte anders heissen als LINECALLBACK wenn du das schon deklariert hast)


Peter18 - Mi 17.06.15 17:16

Hallo Daniel,

endlich kann ich mich auch mit dieser Antwort befassen. Dank Dir auch dafür!

Da bei MS

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
VOID FAR PASCAL lineCallbackFunc(
   DWORD hDevice,
   DWORD dwMsg,
   DWORD dwCallbackInstance,
   DWORD dwParam1,
   DWORD dwParam2,
   DWORD dwParam3
);
steht habe ich alles als "DWORD" deklariert. Die Ergebnisse waren ok. Aber vielleicht macht "PASCAL" den Untreschied. In meinem C++ Buch steht davon nichts, aber vielleicht hätte ich das Internet bemühen sollen.

Auf jeden Fall konnte ich das Ganze Übersetzen. Werd mal Testen und Berichten.

Grüße von der Nordsee wo es nach Regen aussieht

Peter


Peter18 - Do 18.06.15 11:19

Hallo Daniel,

die "CallBack" heißt bei mir jetzt "Line_CallBack" und hat Parameter wie in der "Tapi.pas". Es wird mir auch ein Anruf gemeldet. Manchmal ein Zweiter. Dann ist Sendepause. Erst wenn ich das Programm neu starte wird die CallBack wieder aufgerufen. Zur Kontrolle hatte ich mir einen tapibasierten Anrufmonitor aus dem Internet geholt. Der verhält sich genau so, wenn mein Programm läuft. Läuft er allein, wird aber jeder Anruf gemeldet. Offenbar zersägt irgendetwas die TAPI. Kann es sein, dass der Kompiler ein Problem mit der CallBack hat??

Grüße von der wolkigen Nordsee

Peter


SMO - Do 18.06.15 15:36

"PASCAL" ist die Aufrufkonvention, entspricht "stdcall".
Nein, der Kompiler hat kein Problem mit Callbacks. Wenn es nicht richtig funktioniert, muss noch irgendwo ein Fehler sein.


jaenicke - Do 18.06.15 16:40

user profile iconSMO hat folgendes geschrieben Zum zitierten Posting springen:
"PASCAL" ist die Aufrufkonvention, entspricht "stdcall".
Nein, bei Pascal werden die Parameter von links nach rechts auf dem Stack abgelegt, bei stdcall von rechts nach links!
Es ist also hier wichtig das passende zu benutzen, sonst kommen die Parameter verkehrt herum an, sprich die falschen Werte werden an die Parameter übergeben.


Peter18 - Do 18.06.15 16:55

Hallo SMO,

Dank Dir für die Antwort. Irgend wo anders ein Fehler? Preisfrage: Aber wo??

Vielleicht stimmt der Ablauf ja nicht und es fehlt irgend ein Schritt. Ich hoffe jemand kann mir dazu einen Tipp geben. In "Windows Telephony Programming" habe ich nichts dazu gefunden.


Quelltext
1:
2:
3:
4:
5:
6:
Der Ablauf ist folgender:
  lineInitialize,
  lineOpen für alle "voice-Geräte"
In der "Line_CallBack":
  eingehender Anruf:  lineGetCallInfo ==> Rufnummern
  LINECALLSTATE_IDLE: lineDeallocateCall

Muß ich vielleicht ein CloseLine und dann wieder lineOpen machen???

Grüße von der Nordsee

Peter


Peter18 - Do 18.06.15 17:03

Hallo Sebastian,

Dank Dir für die Klärung. Paßt das Ganze denn dann überhaupt zusammen? Die Reihenfolge scheint ja zu stimmen, denn bei "R := lineDeallocateCall( hDevice );" ist R = 0, also kein Fehler.

grüße von der Nordsee

Peter


SMO - Do 18.06.15 17:13

user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
Nein, bei Pascal werden die Parameter von links nach rechts auf dem Stack abgelegt, bei stdcall von rechts nach links!

Stimmt, hatte ich ganz vergessen! Allerdings behandelt Win32 "PASCAL" als Synonym für "WINAPI", was beides "__stdcall" in C entspricht (siehe [https://msdn.microsoft.com/en-us/library/wda6h6df.aspx], und hier [https://msdn.microsoft.com/en-us/library/aa278672%28v=vs.60%29.aspx]).

Die Frage ist also, stimmt die TTAPICallback Deklaration mit "stdcall" oder müsste es tatsächlich "pascal" sein? Ich habe TAPI noch nie benutzt, aber ich vermute stdcall wird schon korrekt sein.


jaenicke - Do 18.06.15 17:31

In der API Beschreibung wird stdcall gemeint sein. Das fiese ist, dass pascal eigentlich die Bezeichnung für die Übergabe auf dem Stack ist. Stdcall ist eine Unterform, die die Parameter umgekehrt übergibt.
Pascal und stdcall gibt es in Delphi in dieser Form. In C gibt es aber nur cdecl und stdcall, so dass dort mit Pascal die Übergabe auf dem Stack gemeint ist, aber eben in Form der Unterform stdcall.

Stdcall wird also stimmen.


Peter18 - Fr 19.06.15 12:00

Ein freundliches Hallo an alle,

nachdem die Unsicherheit bei der CallBack einigermaßen beseitigt war, habe ich die Meldungen geprüft, die noch nicht bearbeitet wurden. Es zeigte sich, dass nach dem Trennen der Verbindung "LINECALLSTATE_ONHOLD" und dann "LINECALLSTATE_IDLE" mitgeteilt wird. Anscheinend ist "LINECALLSTATE_ONHOLD" die Ursache. Danach wird die Leitung anscheinend offen gehalten, um weitere Aktionen wie Dial durchzuführen. Lt. MS sollte dieser Status nicht auftauchen:
Zitat:
The new call state. This parameter must be one and only one of the following LINECALLSTATE_ constants.
( https://msdn.microsoft.com/en-us/library/windows/desktop/ms736520%28v=vs.85%29.aspx ). Nachdem ich nun die Leitung schließe und erneut öffne funktioniert es wie gewünscht.

Grüße von der regenrischen Nordsee

Peter


uall@ogc - Fr 19.06.15 14:24

Wenn

https://msdn.microsoft.com/en-us/library/ms735512%28v=vs.85%29.aspx

und

https://de.wikipedia.org/wiki/Aufrufkonvention

stimmt dann ist das eine umgedrehte stdcall (?):


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
procedure lineCallbackFunc(
   DWORD dwParam3,
   DWORD dwParam2,
   DWORD dwParam1,
   DWORD dwCallbackInstance,
   DWORD dwMsg,
   DWORD hDevice
); cdecl;


In einem anderen MS Artikel wurde auf die Funktion

https://msdn.microsoft.com/en-us/library/ms735757%28v=vs.85%29.aspx

verwiesen.

Edit: auf wiki steht dort "cdecl + Parameter links->rechts + Funktion selber aufraemen" das sollte dann einem "stdcall + Parameter links->rechts" entsprechen, ist dort echt mies beschrieben


SMO - Fr 19.06.15 14:58

user profile iconuall@ogc hat folgendes geschrieben Zum zitierten Posting springen:
dann ist das eine umgedrehte stdcall (?)


Das haben jaenicke und ich doch bereits geklärt. Ja, die Aufrufkonvention "pascal" (Delphi) bzw. "__pascal" (C) ist ein "umgedrehtes stdcall".
Windows 3.x (16 bit) hat das wohl auch tatsächlich benutzt. In der 32 bit Windows API findet man noch immer gelegentlich "PASCAL" Definitionen, wohl aus historischen Gründen, aber dieses "PASCAL" ist eben nicht "__pascal". In der WinAPI Headerdatei WinDef.h wird nämlich "PASCAL" als "__stdcall" definiert (gleiches gilt für WINAPI und CALLBACK).

Es stimmt also alles, die TAPI "lineCallbackFunc" ist stdcall.