Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Tapi Callback Funktion darf nicht in Klasse sein (https://www.delphipraxis.net/171806-tapi-callback-funktion-darf-nicht-klasse-sein.html)

Jumpy 26. Nov 2012 12:01

Tapi Callback Funktion darf nicht in Klasse sein
 
Hallo,

ich versuche eine vorhandene Anwendung objektorientiert neu zu erstellen, die sich über die Tapi mit unserer Telefonanlage beschäftigt. Ich habe als Objekte Lines, Devices und Calls erstellt und möchte nun als letztes ein Objekt erstellen, dass diese anderen Objekte verwalten kann, wenn man so will das TapiApp-Objekt.
Dieses soll auch die Tapi initialisieren und und die Zugriffe darauf steuern. Das Problem ist, das beim initialisieren der Tapi, dieser eine Callback-Funktion übergeben werden muss, die aber nicht in einer Klasse liegen darf!!!
Eine entsprechende nun nackt im Implememntation-Teil erstellte Funktion ist natürlich kein Problem und wird auch aufgerufen. Nur soll die Funktion ja auch alles gemeldete an das TapiApp Objekt weitergeben, aber wie kann ich das (ausser mit einer globalen Variable für das TapiApp Objekt) machen?

DeddyH 26. Nov 2012 12:13

AW: Tapi Callback Funktion darf nicht in Klasse sein
 
Wie sieht der Callback denn aus? Evtl. kann man da ein wenig tricksen.

Nersgatt 26. Nov 2012 12:14

AW: Tapi Callback Funktion darf nicht in Klasse sein
 
Ich würde das Tapi-Object vermutlich als Singleton implementieren. Der Unterschied zur globalen Variable ist natürlich nicht so wirklich groß, aber es ist doch etwas objektorientierter. Wenn mehrere Instanzen von Klassen informiert werden müssen, dann musst Du noch einen Observer implementieren. Die melden sich an der Singletonklasse an und werden von ihr über die Statusänderungen informiert.

Sir Rufo 26. Nov 2012 12:15

AW: Tapi Callback Funktion darf nicht in Klasse sein
 
Delphi-Quellcode:
unit MyTapiObj;

interface

type
  TMyTapiObj = class
  protected
    procedure CalledFromCallBack;
  public
    constructor Create;
    destructor Destroy; override;
  end;

implementation

var
  _TapiInitialized : Boolean;
  _MyTapiObjList : TObjectList;

procedure TapiCallBack;
var
  LIdx : Integer;
begin
  for LIdx := 0 to Pred( _MyTapiObjList ) do
    ( _MyTapiObjList[LIdx] as TMyTapiObj ).CalledFromCallBack;
end;

procedure TapiInitialize;
begin
  if not _TapiInitialized then
  begin

    // Initialization code goes here

    _TapiInitialized := True;
  end;
end;

constructor TMyTapiObj.Create;
begin
  inherited;
  TapiInitialize;
  _MyTapiObjList.Add( Self );
end;

destructor TMyTapiObj.Destroy;
begin
  _MyTapiObjList.Remove( Self );
  inherited;
end;

initialization

  _MyTapiObjList := TObjectList.Create( False );

finalization

  _MyTapiObjList.Free;

end.

Zacherl 26. Nov 2012 12:20

AW: Tapi Callback Funktion darf nicht in Klasse sein
 
Alternativ mal hier in der DP nach MakeProcInstance suchen. Diese Funktion generiert dir einen klassischen Funktionspointer, der aber dafür sorgt, dass dein Callback im Kontext der Objektinstanz ausgeführt wird.

himitsu 26. Nov 2012 12:52

AW: Tapi Callback Funktion darf nicht in Klasse sein
 
Wenn es keine Methode sein darf, dann kann man das dennoch als statische Klassenmethode deklarieren, welche die selbe Signaur besitzt, wie eine normale Funktion/Prozedur.

Delphi-Quellcode:
type
  TIrgendwas = class
    class function MyCallback(P: PAnsiChar): Integer; stdcall; static;
  end;

// entspricht

function MyCallback(P: PAnsiChar): Integer; stdcall;
static lässt bei Klassenmethoden das Self weg.

Sir Rufo 26. Nov 2012 12:58

AW: Tapi Callback Funktion darf nicht in Klasse sein
 
@himitsu

Kann Delphi 6 schon class procedures?

himitsu 26. Nov 2012 13:04

AW: Tapi Callback Funktion darf nicht in Klasse sein
 
Hmmmmm, müßte man ausprobieren.
D7 kannte das (glaub ich) zumindestens schon.

(wäre ja zu toll, wenn sowas in der OH stünde ... so wie beim MSDN :angle:)

Uwe Raabe 26. Nov 2012 13:30

AW: Tapi Callback Funktion darf nicht in Klasse sein
 
Eigentlich ist das nicht unbedingt verkehrt, wenn hier Lösungen zu der Frage gepostet werden, die mit der Delphi-Version des Fragenden gar nicht umgesetzt werden können. Wenn jemand nach einem ähnlichen Problem sucht, mag der ja durchaus eine neuere Delphi-Version haben. Man muss sich ja nicht unnötigerweise die Sourcen verrenken.

sh17 26. Nov 2012 13:41

AW: Tapi Callback Funktion darf nicht in Klasse sein
 
Zitat:

Zitat von Sir Rufo (Beitrag 1193008)
Kann Delphi 6 schon class procedures?

ja, funktioniert bereits in Delphi 6

Jumpy 26. Nov 2012 14:11

AW: Tapi Callback Funktion darf nicht in Klasse sein
 
Aus der SDK über die Tapi:

Zitat:

The lineCallbackFunc function is a placeholder for the application-supplied function name.

VOID FAR PASCAL lineCallbackFunc(

DWORD hDevice,
DWORD dwMsg,
DWORD dwCallbackInstance,
DWORD dwParam1,
DWORD dwParam2,
DWORD dwParam3
);


Parameters

hDevice

A handle to either a line device or a call associated with the callback. The nature of this handle (line handle or call handle) can be determined by the context provided by dwMsg. Applications must use the DWORD type for this parameter because using the HANDLE type may generate an error.

dwMsg

A line or call device message.

dwCallbackInstance

Callback instance data passed back to the application in the callback. This DWORD is not interpreted by TAPI.

dwParam1

A parameter for the message.

dwParam2

A parameter for the message.

dwParam3

A parameter for the message.



Remarks

For information about parameter values passed to this function, see Line Device Messages.
All callbacks occur in the application's context. The callback function must reside in a DLL or application module.
Eine andere Bemerkung dazu, die ich gefunden hatte. Heißt das nun, Statische Methode oder Klassenmethode ist OK, oder auch nicht?

Zitat:

Additionally, the callback function must be a global procedure, not a class member (method). The problem in substituting a callback function by a class member is that Windows does not understand either C++ class member functions or Delphi's methods which carry "this" and "self" as a first hidden parameter.

Bummi 26. Nov 2012 14:29

AW: Tapi Callback Funktion darf nicht in Klasse sein
 
IMHO hat nur Zacherl die passende Lösung angeboten ...

himitsu 26. Nov 2012 14:29

AW: Tapi Callback Funktion darf nicht in Klasse sein
 
Wie gesagt, eine statische Klassenmethode hat kein Self/this, also kann sie wie eine normale "globale" Funktion verwendet werden.

Eine Methoden und eine normale Klassenmethode haben aber einen "versteckten" noch Self-Parameter, außerdem bestehen diese Methoden-Zeiger aus zwei "Zeigern" (die Codeadresse und das Self),
wärend normale Prozeduren und statische Klassenmethoden nur einen Zeiger zur Adressierung benötigen.


Im Prinzip ist eine statische Klassenmethode eine "normale" Prozedur (von der Parametern her), nur daß sie "optisch" in einer Klasse drinsteckt. Sie kann auch nicht dynamisch virtuell, abstrakt sein.
Es ist halt ein "Design"-Element, um seinen Code aufräumen zu können.

Uwe Raabe 26. Nov 2012 14:35

AW: Tapi Callback Funktion darf nicht in Klasse sein
 
Du könntest die Klasseninstanz in dwCallbackInstance mitgeben (oder wie der bei den Methodenaufrufen auch heissen mag). Dann bekommst du diese Instanz in der Callback-Methode wieder mit und kannst die passende Instanz aufrufen. Wenn du jetzt noch ein Beispiel für eine Methode zeigst, die du aufrufst, dann könnte man ein Beispiel fertig machen.

Der Hinweis auf die Delphi-Methoden gilt übrigens nicht für statische Klassenmethoden, weswegen die auch als Lösung gehen (siehe Himitsu's Beispiel). Damit kann man dann die ganze API in eine Klasse wrappen, braucht keine globalen Prozeduren und keine globale Singleton-Instanz.

DeddyH 26. Nov 2012 14:38

AW: Tapi Callback Funktion darf nicht in Klasse sein
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1193037)
Du könntest die Klasseninstanz in dwCallbackInstance mitgeben (oder wie der bei den Methodenaufrufen auch heissen mag). Dann bekommst du diese Instanz in der Callback-Methode wieder mit und kannst die passende Instanz aufrufen. Wenn du jetzt noch ein Beispiel für eine Methode zeigst, die du aufrufst, dann könnte man ein Beispiel fertig machen.

Genau darauf zielte meine Frage in #2 ab. Man packt also die eigene Instanz in den Parameter und kann in der Callback-Routine nach einem Cast wieder auf die Methoden und Properties dieser Instanz zugreifen.

Jumpy 26. Nov 2012 14:38

AW: Tapi Callback Funktion darf nicht in Klasse sein
 
Mein Delphi 6 kennt zwar scheinbar "class function" aber nicht "static". Womit Himis Ansatz für mich wohl nicht funktioniert.

Zacherls Ansatz werd ich mal suchen, doch die ersten 2-3 Post dazu haben mich schon verwirrt, aber mal sehen.

Sir Rufos Ansatz sieht aber auch machbar aus, nutzt dann aber auch einen globalen Ansatz (wenn auch nicht eine globale Variable vom TapiApp-Object exisiert, was schonmal eine Verbesserung gegenüber meiner Notlösung ist).

Sir Rufo 26. Nov 2012 14:59

AW: Tapi Callback Funktion darf nicht in Klasse sein
 
@Jumpy

Zeig doch mal wie der CallBack initialisiert wird. Es scheint, du musst da die Instanz mitgeben, denn dann brauchst du keine Liste über die Instanzen mitführen.

Irgendwoher muss ja die dwCallbackInstance herkommen und die kannst du in der globalen proc auswerten, damit du weißt wohin der Aufruf gehen soll.

Hab gerade mal gegooglet und dwCallbackInstance ist wohl der Handle zur App, also wohl doch selber verwalten, wenn die Nachricht an mehrere interne Instanzen soll.

Jumpy 26. Nov 2012 15:05

AW: Tapi Callback Funktion darf nicht in Klasse sein
 
Liste der Anhänge anzeigen (Anzahl: 1)
@DeddyH/Uwe: Ich verstehe leider nicht genau, worauf ihr bei eurer Frage hinauswollt und welche Infos ihr noch braucht. Ich häng mal die Highlights der Unit wie sie bisher war, mit globaler Variable usw. hier an:
Die externe Callback-Prozedur benutzt die globale Variable MyTapi um, wenn aufgerufen, auf das MyTapi-Objekt zuzugreifen und z.B. in diesem Objekt registrierte andere Callback-Routinen zu feuern usw.Eine andere solche Callback-Routine steht in der Main-Unit, die das MyTapi-Objekt erstellt und das die Nachrichten, die über die eigene Callback kommen für die Geschäftslkogik benutzt.

Edit: Als Dateianhang die Unit Tapi, eine Tapi-Kapselung von den Jedis?

Delphi-Quellcode:
unit uMyTapiObj;

interface

uses tapi,
     Dialogs, SysUtils, ShellAPI, windows, Forms;

const
  {$IFDEF Win32}
  TapiDll = 'tapi32.dll';
  {$ELSE}
  TapiDll = 'tapi.dll';
  {$ENDIF}
  TAPI_HiVer = $00030000; // Highest API version wanted (3.0)
  TAPI_LoVer = $00010004; // Lowest API version accepted (1.4)
  TAPI_NegHi = $00030000; // High API Version for negotiation purposes
  TAPI_NegLo = $00010000; // Low API Version for negotiation purposes

  maxBufSize = $3FF;      // Größe des Datenbuffers für Datenrückgabe
                           // (Stichwort: Variabler Teil für Strings)

  LINE_PROGRAMCHECK = 127; // Virtuelle TAPI_Message für Timer-gesteuerte Checks
 
type

  TLineCallbacks = array of TLineCallBack;       // Nimmt die Adressen der gesetzten Callbackroutinen auf

 
  //****************************************************************************
  // TMyTapi: Klasse für die Tapi selbst
  // die Instanz MyTapi muss created und initialized werden, da diese
  // den callback mechanismus implementiert.
  // Beim Aufruf von initialize muss ein Pointer auf die Callback Routine
  // im aufrufenden Modul übergeben werden.
  //****************************************************************************
  TMyTapi = class(TObject)
  private
    priv_Initialized: boolean;
    priv_LineApp: HLINEAPP;
    priv_hInstance: Cardinal;
    priv_IDStr: PChar;
    priv_NumDevs: Cardinal;
    priv_Version: Cardinal;
    priv_LineInitializeExParams: LineInitializeExParams_tag;
    priv_LineCallback: TLineCallbacks;
    priv_CallbackCount: integer;
    function AddCall(Line: integer; CallHandle: hCall): boolean;
    function RemoveCall(CallHandle: hCall): boolean;
  public
    constructor Create;                                                  
    destructor Destroy; override;                                            
    procedure AddCallback(CallbackProcedure: TLineCallback);                         // Hinzufügen einer Callback Routine
    procedure RemoveCallBack(CallbackProcedure: TLineCallback);                      // Entfernen einer Callback Routine
    property CallbackCount: integer read priv_CallbackCount;                         // Anzahl der hinzugefügten Callback Routinen
    property ID_Str: PChar read priv_IDStr;                                              
    function Initialize: boolean;                                                    
    property Initialized: boolean read priv_Initialized;
    property LineApp: HLINEAPP read priv_LineApp;                                    
    property NumDevs: cardinal read priv_NumDevs;                                    
    property TAPI_Version: string read get_Version;                                  
  end;


//******************************************************************************
// MyTapi muß erzeugt und initialisiert werden !!!
// wird von allen Objekten benutzt um auf Tapi Funktionen zuzugreifen
//******************************************************************************
var
  MyTapi: TMyTapi;

implementation

//******************************************************************************
// Master Callback Routine
// - Kümmert sich um neue Calls und löscht diese bei LINECALLSTATE_IDLE
// - ruft alle registrierten callback Routinen auf
//******************************************************************************
procedure priv_MyCallback(hDevice,
                          dwMsg,
                          dwCallbackInstance,
                          dwParam1,
                          dwParam2,
                          dwParam3: Cardinal); stdcall;
var i: integer;
begin
 try
 case dwMsg of
  LINE_PROGRAMCHECK: begin

      end;
  LINE_APPNEWCALL: begin
      if MyTapi.priv_SupportCalls
         then MyTapi.AddCall(dwCallbackInstance, dwParam2);
      end;
  LINE_CALLSTATE: begin
       if MyTapi.priv_SupportCalls then begin
        i := low(MyTapi.priv_calls);
        while (MyTapi.priv_calls[i].priv_CallHandle <> hDevice) and (i < high(MyTapi.priv_calls)) do
              inc(i);
        if MyTapi.priv_calls[i].priv_CallHandle = hDevice then begin
         if (dwParam1 = LINECALLSTATE_IDLE)
            then MyTapi.RemoveCall(hDevice)
            else MyTapi.priv_Calls[i].initialize(hDevice);
        end else begin
         MyTapi.AddCall(dwCallbackInstance, hDevice);
        end;
       end;
      end;
  LINE_CALLINFO: begin
       if MyTapi.priv_SupportCalls then begin
        i := low(MyTapi.priv_calls);
        while (MyTapi.priv_calls[i].priv_CallHandle <> hDevice) and (i < high(MyTapi.priv_calls)) do
              inc(i);
        if MyTapi.priv_calls[i].priv_CallHandle = hDevice then begin
         MyTapi.priv_Calls[i].initialize(hDevice);
        end;
       end;
      end;
  LINE_CLOSE,
  LINE_CREATE,
  LINE_REMOVE: begin
        (*if (*)shellexecute(Application.Handle,
                         'open',
                         PChar(ParamStr(0)),
                         nil,
                         PChar(ExtractFilePath(ParamStr(0))),
                         SW_SHOWNORMAL)(* <= 32)
           then ShowMessage('Bitte Telefonliste neu starten!')*);
        Application.MainForm.Close;
        Application.Terminate;
       end;
  else begin
//        MyTapi.ShowWarning('Code: ' + IntToStr(dwMsg) +
//                           ', Line: ' + IntToStr(dwCallbackInstance), 'priv_MyCallback');
       end;
 end;

 if MyTapi <> nil then if MyTapi.Initialized then begin
  for i := 0 to MyTapi.CallbackCount - 1 do begin
   MyTapi.priv_LineCallback[i](hDevice, dwMsg, dwCallbackInstance,
                               dwParam1, dwParam2, dwParam3);
  end;
  if MyTapi.priv_debugging
     then
      try
       MyTapi.priv_LogDebug(Tapi_Messages[dwMsg], IntToStr(dwCallbackInstance));
      except
      end;
 end;
 except
   MyTapi.ShowWarning('Error: priv_MyCallback', 'uMyTapiObj');
 end;
end;



{ TTapiSP }

constructor TMyTapi.Create;
begin
 inherited Create;
 priv_Initialized := false;
 priv_IDStr := 'Test';
 priv_hInstance := hInstance;
 nd;

destructor TMyTapi.Destroy;
begin
 LogErrors := false;
 priv_LineCallback := nil;
 inherited Destroy;
 MyTapi := nil;
end;

function TMyTapi.get_Version: string;
begin
 Result := IntToStr(priv_Version div $10000) + '.'
         + IntToStr(priv_Version mod $10000);
end;

function TMyTapi.Initialize: boolean;
var i: integer;
begin
 Result := false;
 try
  // initialisieren der MyLineInitializeExParams
  FillChar(priv_LineInitializeExParams, SizeOf(priv_LineInitializeExParams), 0);
  with priv_LineInitializeExParams do begin
   dwTotalSize := sizeof(priv_LineInitializeExParams);
   // welcher Signalisierungstyp wird benutzt:
   // LINEINITIALIZEEXOPTION_USEHIDDENWINDOW
   // LINEINITIALIZEEXOPTION_USEEVENT
   // LINEINITIALIZEEXOPTION_USECOMPLETIONPORT
   dwOptions := LINEINITIALIZEEXOPTION_USEHIDDENWINDOW;
  end;
  priv_Version := TAPI_NegHi;
  try
   // Verbindungsaufnahme mit der TAPI
   // - TAPI Version aushandeln, die maximal benutzt werden kann
   // Callback Routine registrieren
   // Anzahl der Lines
   priv_Initialized := (lineInitializeEX(@priv_LineApp,
                                         priv_hInstance,
                                         priv_MyCallback,
                                         priv_IDStr,
                                         priv_NumDevs,
                                         priv_Version,
                                         priv_LineInitializeExParams) >= 0);
  except
   priv_Initialized := false;
  end;
  if priv_Initialized then begin
   SetLength(priv_Lines, priv_NumDevs);
   for i := Low(priv_lines) to High(priv_lines) do begin
    priv_Lines[i] := TMyInnoLine.Create;
    priv_Lines[i].Initialize(i);
    if priv_Lines[i].isInno and priv_SupportCalls
       then priv_Lines[i].LookForNewCalls;
   end;
  end;
  Result := priv_Initialized;
 except
  ShowWarning('Error: TMyTapi.Initialize', 'uMyTapiObj');
 end;
end;

//******************************************************************************
// Einen neuen Call in die Liste aufnehmen
//******************************************************************************
function TMyTapi.AddCall(Line: Integer; CallHandle: hCall): boolean;
var i: integer;
    newLength: integer;
begin
 try
  if priv_SupportCalls then begin
   inc(priv_CallCount);
   SetLength(priv_Calls, priv_CallCount);
   i := high(priv_calls);
   priv_Calls[i] := TMyCall.Create;
   Result := priv_Calls[i].initialize(CallHandle);
   if Result then begin
    newLength := High(priv_Lines[Line].priv_MyCalls) + 2;
    SetLength(priv_Lines[Line].priv_MyCalls, newLength);
    priv_Lines[Line].priv_MyCalls[newLength - 1] := priv_Calls[i];
   end;
  end else begin
   Result := false;
   ShowWarning('Support of Calls disabled', 'MyTapi.AddCall');
  end;
 except
  Result := false;
  ShowWarning('Error: TMyTapi.AddCall', 'uMyTapiObj');
 end;
end;

//******************************************************************************
// Einen bestehenden Call aus der Liste löschen
//******************************************************************************
function TMyTapi.RemoveCall(CallHandle: hCall): boolean;
var i, j: integer;
    i2, j2, k2: integer;
begin
 try
  if priv_SupportCalls then begin
   i := low(priv_calls);
   while (priv_calls[i].priv_CallHandle <> CallHandle) and (i < high(priv_calls)) do
         inc(i);
   if priv_calls[i].priv_CallHandle = CallHandle then begin
    for i2 := Low(priv_Lines) to High(priv_Lines) do begin
     for j2 := Low(priv_Lines[i2].priv_MyCalls) to High(priv_Lines[i2].priv_MyCalls) do begin
      if priv_Lines[i2].priv_MyCalls[j2].priv_CallHandle = CallHandle then begin
       for k2 := j2 to High(priv_Lines[i2].priv_MyCalls) - 1 do begin
        priv_Lines[i2].priv_MyCalls[k2] := priv_Lines[i2].priv_MyCalls[k2 + 1];
       end;
       SetLength(priv_Lines[i2].priv_MyCalls, High(priv_Lines[i2].priv_MyCalls));
      end;
     end;
    end;

    priv_calls[i].Destroy;
    for j := i to high(priv_Calls) - 1
     do priv_Calls[j] := priv_Calls[j + 1];
    dec(priv_CallCount);
    Setlength(priv_Calls, priv_Callcount);
    Result := true;
   end else begin
    Result := false;
   end;
   if Result then begin
   end;
  end else begin
   Result := false;
   ShowWarning('Support of Calls disabled', 'MyTapi.RemoveCall');
  end;
 except
  Result := false;
  ShowWarning('Error: TMyTapi.RemoveCall', 'uMyTapiObj');
 end;
end;

//******************************************************************************
// Eine Callback Routine registrieren
//******************************************************************************
procedure TMyTapi.AddCallback(CallbackProcedure: TLineCallback);
begin
 try
  inc(priv_CallbackCount);
  SetLength(priv_LineCallback, priv_CallbackCount);
  priv_LineCallback[High(priv_LineCallback)] := CallbackProcedure;
 except
  ShowWarning('Error: TMyTapi.AddCallback', 'uMyTapiObj');
 end;
end;

//******************************************************************************
// Registrierung einer Callback Routine entfernen
//******************************************************************************
procedure TMyTapi.RemoveCallBack(CallbackProcedure: TLineCallback);
var i, j: integer;
begin
 try
  i := Low(priv_LineCallback);
  while (@priv_LineCallback[i] <> @CallbackProcedure) and (i < High(priv_LineCallback))
        do inc(i);
  if @priv_LineCallback[i] = @CallbackProcedure then begin
   for j := i to High(priv_LineCallback) - 1
       do priv_LineCallback[j] := priv_LineCallback[j + 1];
   dec(priv_CallbackCount);
   SetLength(priv_LineCallback, priv_CallbackCount);
  end else begin
   ShowWarning('Warning: can''t remove Callback!', 'uMyTapiObj');
  end;
 except
  ShowWarning('Error: TMyTapi.RemoveCallback', 'uMyTapiObj');
 end;
end;

function TMyTapi.getMemStr(Buffer: TBuffer; Offset, Size: Cardinal): string;
var s: string;
    c: char;
    i: integer;
begin
  s := '';
  try
   try
    for i := Offset to Offset + Size - 1 do begin
     c := Buffer[i];
     if c in [#0..#31]
        then c := ' ';
     s := s + c;
    end;
   except
    ShowWarning('Error: TMyTapi.getMemStr', 'uMyTapiObj');
    raise;
   end;
  finally
   Result := s;
  end;
end;

end.
Auszug aus Main-Unit
Delphi-Quellcode:
unit aMain;

interface

uses
  Projekt, uMyTapiObj, Tapi,...

type
  TMain = class(TForm)
    ActionList1: TActionList;
    MainMenu1: TMainMenu;
    StatusBar: TStatusBar;
    //usw
  end;

procedure MyCallback(hDevice, dwMsg, dwCallbackInstance, dwParam1, dwParam2, dwParam3: Cardinal); stdcall;

var
  Main: TMain;

implementation

procedure MyCallback(hDevice, dwMsg, dwCallbackInstance,
                     dwParam1, dwParam2, dwParam3: Cardinal); stdcall;
begin
 case dwMsg of
  LINE_CALLSTATE: begin
        case dwParam1 of
         LINECALLSTATE_IDLE: begin
               Main.ChangeLineState(dwCallbackInstance, ReplaceMessage);
              end;
         LINECALLSTATE_OFFERING: begin
               Main.ChangeLineState(dwCallbackInstance, ReplaceMessage, 'neuer Anruf', ShowNumbers);
              end;
         LINECALLSTATE_ACCEPTED: begin
               Main.ChangeLineState(dwCallbackInstance, ReplaceMessage, 'klingelt', ShowNumbers);
              end;
         LINECALLSTATE_DIALTONE: begin
               Main.ChangeLineState(dwCallbackInstance, ReplaceMessage, 'Wählton'); //hier
              end;
         LINECALLSTATE_DIALING,
         LINECALLSTATE_PROCEEDING: begin
               Main.ChangeLineState(dwCallbackInstance, ReplaceMessage, 'wählt'); //hier
              end;
         LINECALLSTATE_RINGBACK: begin
               Main.ChangeLineState(dwCallbackInstance, ReplaceMessage, 'Ziel erreicht', ShowNumbers);
              end;

//usw.
end;

procedure TMain.FormCreate(Sender: TObject);
var i:Integer;
begin
//...
  MyTapi := TMyTapi.Create;
  if MyTapi.Initialize then
    begin
    MyTapi.AddCallback(amain.MyCallback);
    end
  else
    //
//...
end;
Also auch sieht das irgendwie unschön aus.

DeddyH 26. Nov 2012 15:19

AW: Tapi Callback Funktion darf nicht in Klasse sein
 
Das ist mir etwas zu viel Code zum schnellen Suchen, aber wird dwCallbackInstance nirgends gesetzt? Wenn die nirgends übergeben werden kann, ist ja der Parameter sinnlos.

Jumpy 26. Nov 2012 15:30

AW: Tapi Callback Funktion darf nicht in Klasse sein
 
Hab mich etwas selber verwirrt und daher eure Fragen nicht verstanden:

dwCallbackInstance kann u.a. mit irgend so einer anderen Tapi-Funktion gesetzt werden, aber die Tapi-Schnittstelle(?) unserer Telefonanlage unterstützt das nicht, d.h. da kommt immer 0 zurück. Scheinbar ist das von Anlage zu Anlage untersch. welche Tapi-Funktionalitäten genau unterstützt werden. D.h. da ist nix zu holen.

Mein Ziel ist es halt, das die sich nicht innerhalb einer Klasse befindliche CallBack-Prozedur, die ich der tapi bei der intitialisierung übergebe(n muss), die gefeuerten Events / Nachrichten, an meine Klasse weitergibt. Notfalls wohl dann wie bei Sir Rufo, wo die Callback auf eine global deklarierte Liste zugreifen kann in der sich (im meinem Fall nur die) eine Klasse registrieren kann, an die die Callbacks weitergeleitet werden sollen.

Der Ist-Zusatand den wir momentan haben gefällt mir halt nicht, z.B. welche Objekte wir da haben und wie die zueinander stehen. AUch fehlen mit Funktinalitäten, bisher wird die Anlage nur überwacht. Jetzt soll sie damit auch gesteuert werden können (z.B. Rufe umleiten usw.) Dazu bau ich das halt aus und um und wollt das irgendwie "sauber" hinkriegen, ohne die globale Variable MyTapi.

Bei Forms mach ich das auch so, ausser der Main-Form lösch ich immer alle diese Variablen ala Form2:TForm2 usw. aus den Units und leg mir entsprechedne Variablen da an wo ich das Form aufrufen will, und das wollt ich halt hier auch machen.

Sir Rufo 26. Nov 2012 15:32

AW: Tapi Callback Funktion darf nicht in Klasse sein
 
Zitat:

Zitat von Jumpy (Beitrag 1193045)
Mein Ziel ist es halt, das die sich nicht innerhalb einer Klasse befindliche CallBack-Prozedur, die ich der tapi bei der intitialisierung übergebe(n muss), die gefeuerten Events / Nachrichten, an meine Klasse weitergibt. Notfalls wohl dann wie bei Sir Rufo, wo die Callback auf eine global deklarierte Liste zugreifen kann in der sich (im meinem Fall nur die) eine Klasse registrieren kann, an die die Callbacks weitergeleitet werden sollen.

Aber das machst du doch schon ... du verwaltest doch schon eine Liste mit Callbacks

Sir Rufo 26. Nov 2012 15:42

AW: Tapi Callback Funktion darf nicht in Klasse sein
 
Allerdings hätte ich auch einiges an Verbesserungen zum Code:
  1. MyTapi ist ein Singleton und sollte auch entsprechend implementiert werden z.B. mit einem Interface
    Delphi-Quellcode:
    interface

    type
      IFoo = interface
        ['{10C358B9-FCF2-4CAF-9F35-65DDDD4C6039}']
        procedure DoSomething;
      end;

    function MyFoo : IFoo;

    implementation

    type
      TFoo = class( TInterfacedObject, IFoo )
        procedure DoSomething;
      end;

    var
      _MyFoo : IFoo;

    procedure TFoo.DoSomething;
    begin

    end;

    function MyFoo : IFoo;
    begin
      if not Assigned( _MyFoo ) then
        _MyFoo := TFoo.Create;
      Result := _MyFoo;
    end;
  2. Du verwaltest eine Liste mit Callbacks, die dann aufgerufen werden. Warum nimmst du dann nicht Methoden? Du rufst die auf, dann kannst du auch bestimmen, was du aufrufst.
    Delphi-Quellcode:
    type
      TTapiCallbackMethod = procedure ( hDevice, dwMsg, {dwCallbackInstance, } dwParam1, dwParam2, dwParam3 : Cardinal ) of object;

Jumpy 26. Nov 2012 16:07

AW: Tapi Callback Funktion darf nicht in Klasse sein
 
Das oben gezeigte ist auszugsweise der Ist-Zustand, von dem ich ja weg will. Daher sind Verbesserungsvorschläge gerne gesehen. Mein irgendwann fertiges TapiApp-Object soll wie z.Zt. MyTapi eine Liste von Callback-Funktionen verwalten. Diese können dann tatsächlich so aussehen wie ich das will, sprich welche Parameter usw. Zu deinem

Delphi-Quellcode:
type
  TTapiCallbackMethod = procedure ( hDevice, dwMsg, {dwCallbackInstance, } dwParam1, dwParam2, dwParam3 : Cardinal ) of object;
könnte dann vllt. noch ein:
Delphi-Quellcode:
  TTapiCallbackMethods = Array of TTapiCallbackMethod
kommen. Geht das? Oder sind so Methodenzeiger (das ist das doch) besser in einer T(Object)List o.ä. aufgehoben?

-------

Es gibt wenn man so will 2 Arten von Callback Funktionen. Eine "Master"-CallBack-Funktion für die Tapi ausserhalb von Objekten, die aber auch an mein Objekt alles weitergeben soll und dann andere Callback-Funktionen (diesmla gerne auch von anderen Objekten), die in meinem Objekt gelistet sind und an die so auch die ursprüngliche Tapi-Message (ggf. in angepasster Form) weitergegeben wird.

-------
Wenn ich nun MyTapi als Singelton (muss ich mir nochmal genauer anschauen) umsetzte, kann ich das denn auch in meiner aussserhalb von Objekten (nur im Application-Kontext) deklarierten CallFunktion benutzen?

Sir Rufo 26. Nov 2012 16:12

AW: Tapi Callback Funktion darf nicht in Klasse sein
 
Zitat:

Zitat von Jumpy (Beitrag 1193050)
könnte dann vllt. noch ein:
Delphi-Quellcode:
  TTapiCallbackMethods = Array of TTapiCallbackMethod
kommen. Geht das? Oder sind so Methodenzeiger (das ist das doch) besser in einer T(Object)List o.ä. aufgehoben?

Völlig wumpe, die Struktur, wie gespeichert wird, würde ich eh nicht nach aussen geben. Das Interface hat einfach zwei Methoden: AddMethod, RemoveMethod. Den Rest interessiert keinen der von aussen schaut.
Zitat:

Zitat von Jumpy (Beitrag 1193050)
Wenn ich nun MyTapi als Singelton (muss ich mir nochmal genauer anschauen) umsetzte, kann ich das denn auch in meiner aussserhalb von Objekten (nur im Application-Kontext) deklarierten Funktion benutzen?

Öh, ja warum nicht ... das Singleton sorgt nur dafür, dass es max. 1 Instanz gibt, mehr nicht

shmia 26. Nov 2012 17:17

AW: Tapi Callback Funktion darf nicht in Klasse sein
 
Das Weiterleiten einer Callback-Funktion auf die Callback-Methode ist doch ganz einfach!
Delphi-Quellcode:
procedure priv_MyCallback(hDevice,
                          dwMsg,
                          dwCallbackInstance,
                          dwParam1,
                          dwParam2,
                          dwParam3: Cardinal); stdcall;
begin
  if dwCallbackInstance = 0 then
  begin
    // Oh ohhh, das sollte nie vorkommen (wäre ganz klar ein Fehler des Programmierers)
    Assert(False);
  end
  else
  begin
     try
        TMyTapi(dwCallbackInstance).MyCallback(hDevice,
                          dwMsg,
                          dwParam1,
                          dwParam2,
                          dwParam3);
    except
       // Exception loggen oder anzeigen
       // auf jeden Fall sollte keine Exception dem Aufrufer um die Ohren fliegen
    end;
  end;
end;

DeddyH 26. Nov 2012 17:58

AW: Tapi Callback Funktion darf nicht in Klasse sein
 
Das ist doch ziemlich genau das, was ich weiter vorn bereits gesagt habe. Mir ist nur noch nicht klar, wie man dwCallbackInstance belegen kann.

Sir Rufo 26. Nov 2012 18:19

AW: Tapi Callback Funktion darf nicht in Klasse sein
 
Zitat:

Zitat von DeddyH (Beitrag 1193077)
Das ist doch ziemlich genau das, was ich weiter vorn bereits gesagt habe. Mir ist nur noch nicht klar, wie man dwCallbackInstance belegen kann.

Beim Initialisieren gibt man das mit an

Jumpy 27. Nov 2012 10:09

AW: Tapi Callback Funktion darf nicht in Klasse sein
 
Zitat:

dwCallbackInstance
Instance data passed back to the application, which was specified by the application in the dwCallBackInstance parameter of lineInitializeEx. This DWORD is not interpreted by TAPI.
dwCallBackInstance = hInstance aus der Initialisierung?

Zitat:

LONG WINAPI lineInitializeEx(
LPHLINEAPP lphLineApp,
HINSTANCE hInstance,
LINECALLBACK lpfnCallback,
LPCSTR lpszFriendlyAppName,
LPDWORD lpdwNumDevs,
LPDWORD lpdwAPIVersion,
LPLINEINITIALIZEEXPARAMS lpLineInitializeExParams
);

hInstance
Instance handle of the client application or DLL. The application or DLL can pass NULL for this parameter, in which case TAPI uses the module handle of the root executable of the process (for purposes of identifying call handoff targets and media mode priorities).

aus der SysInit:
HInstance: LongWord; { Handle of this instance }
Hab mich heut morgen ein wenig durch die SDK gekämpft und da nichts weiteres dazu gefunden, wo das ausser in lineInitializeEx gesetzt werden könnte. So wie das bei unserem alten Code gesetzt wird, vermute ich da eher einen Handle o.ä. auf die Application:
Delphi-Quellcode:
var priv_hInstance:Cardinal;
    priv_hInstance := hInstance; //hInstance aus Sysinit
    lineInitializeEX(@priv_LineApp,priv_hInstance,priv_MyCallback,//...
Sehe leider nicht, wie ich das gebrauchen kann, um auf mein Objekt zu zeiegn. Werde also versuchen mein TapiApp-Object als Singelton zu implementieren.

himitsu 27. Nov 2012 10:17

AW: Tapi Callback Funktion darf nicht in Klasse sein
 
Zitat:

Zitat von Jumpy (Beitrag 1193174)
Delphi-Quellcode:
var priv_hInstance:Cardinal;
    priv_hInstance := hInstance; //hInstance aus Sysinit
    lineInitializeEX(@priv_LineApp,priv_hInstance,priv_MyCallback,//...
Sehe leider nicht, wie ich das gebrauchen kann, um auf mein Objekt zu zeiegn.

Statt hInstance den Instanz-Zeiger deines Objektes übergeben? :angle2:

Jumpy 27. Nov 2012 10:31

AW: Tapi Callback Funktion darf nicht in Klasse sein
 
Kommando zurück. Hab komplett an der falschen Stelle gesucht. Der Parameter wird bei lineOpen übergeben, das man für jede Telefonleitung, die man überwachen will, aufruft.

Zitat:

LONG lineOpen(
HLINEAPP hLineApp,
DWORD dwDeviceID,
LPHLINE lphLine,
DWORD dwAPIVersion,
DWORD dwExtVersion,
DWORD dwCallbackInstance,
DWORD dwPrivileges,
DWORD dwMediaModes,
LPLINECALLPARAMS const lpCallParams
);

dwCallbackInstance

User-instance data passed back to the application with each message associated with this line or addresses or calls on this line. This parameter is not interpreted by the Telephony API.
Bei uns wurde da scheinbar immer die Nummer der Leitung übergeben, was aber unnötig ist, da man da ja auch den Handle der Leitung bekommt.
Also ist es ja vllt. doch möglich da einen Verweis auf mein Objekt hinzukriegen. Hoffe es hilft mir noch jemand nach der ganzen Verwirrung.

Wenn ich innerhalb meines Objectes für jede Line lineOpen aufrufe, wie kann ich dann dabei einen Pointer(?) auf mein Objekt in dwCallbackInstance mitgeben, das ja vom Typ DWORD ist?

Und wie muss ich dieses DWORD in der Callback-Funktion auswerten, um da wieder auf mein Objekt zu kommen? So wie shmia das andeutet, indem ich das in mein Objekt caste?
TMyTapi(dwCallbackInstance).

DeddyH 27. Nov 2012 10:35

AW: Tapi Callback Funktion darf nicht in Klasse sein
 
Unter Win32 (genauer: in 32Bit-Anwendungen) kannst Du Instanzen problemlos nach DWORD casten und zurück. Also kannst Du bei der Initialisierung dann DWORD(self) übergeben und im Callback dann mit TDeineKlasse(dwCallbackInstance) auf die Instanz zugreifen.


Alle Zeitangaben in WEZ +1. Es ist jetzt 00:23 Uhr.

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz