[Gelöst] Event in Class ausführen.

Für Fragen von Einsteigern und Programmieranfängern...
Antworten
Benutzeravatar
Garfield
Beiträge: 173
Registriert: Do 5. Jun 2008, 22:07
OS, Lazarus, FPC: Ubuntu 22.04 LTS (Laz 3.0 FPC3.2.2)
CPU-Target: 64Bit
Wohnort: Aken

[Gelöst] Event in Class ausführen.

Beitrag von Garfield »

Hallo,

ich möchte eine Delphi-Class unter Lazarus nutzbar machen. Es handelt sich um einen Fritzbox-Callmonitor, welcher mit Delphi unter WinXP ohne Probleme funktioniert. Bei Lazarus habe ich das Problem, dass es bei

Code: Alles auswählen

if Assigned(fOnCallEvent)
then fOnCallEvent(rData, slEvents.Strings[i]);
zu dem äußerst vielsagenden Fehler "External SIGSEGV" kommt. Also bei Assigned und wenn das auskommentiert ist bei fOnCallEvent.

Code: Alles auswählen

type
  TCallEventData = record
    DateTime     : AnsiString;
    EventType    : Integer;   
    ConnectionID : Integer;  
    RemoteNumber : AnsiString; 
    LocalNumber  : AnsiString; 
    Extension    : AnsiString;
    Duration     : Integer;   
  end;  
 
type
  TOnCallEvent = procedure(aCall: TCallEventData; aMsg: AnsiString) of object;
 
type
  TCallMonitor = class(TThread)
  private
    ...
    fOnCallEvent : TOnCallEvent;
    ...
  public
    constructor Create(aOwner:hwnd);
    destructor Destroy;
    ...
    property OnCallEvent: TOnCallEvent read fOnCallEvent write fOnCallEvent;
  end;
Ich habe jetzt mehrmals über mehrere Stunden gesucht und finde keinen Fehler bzw finde nichts was ich ändern muss. Der Client empfängt die Nachricht von der Fritzbox und wenn sie diese als Record und als String weitergegeben werden soll kommt es zu dem Fehler.
Dateianhänge
FritzBox_CallMonitor.7z
(66.35 KiB) 80-mal heruntergeladen
Zuletzt geändert von Garfield am Di 4. Mär 2014, 17:26, insgesamt 2-mal geändert.
Gruß Garfield

Socke
Lazarusforum e. V.
Beiträge: 3178
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: Event in Class ausführen.

Beitrag von Socke »

Garfield hat geschrieben:

Code: Alles auswählen

if Assigned(fOnCallEvent)
then fOnCallEvent(rData, slEvents.Strings[i]);
zu dem äußerst vielsagenden Fehler "External SIGSEGV" kommt. Also bei Assigned und wenn das auskommentiert ist bei fOnCallEvent.
Meinst du die Zeilen 421/422 in der Unit uCallMonitor? Die werden bei mir nie aufgerufen.

Mein Vorgehen:
  • Entpacken
  • Datei Lazrus/Callmonitor.lpi in Lazarus öffnen und Projekt starten
  • Auf Start klicken (Host=fritz.box und Port=1012)
  • Verbindung kann nicht hergestellt werden (uCallMonitor, Zeile 525)
Muss ich noch irgendetwas in meiner Fritzbox (7270) einstellen, damit das geht?
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

Benutzeravatar
Garfield
Beiträge: 173
Registriert: Do 5. Jun 2008, 22:07
OS, Lazarus, FPC: Ubuntu 22.04 LTS (Laz 3.0 FPC3.2.2)
CPU-Target: 64Bit
Wohnort: Aken

Re: Event in Class ausführen.

Beitrag von Garfield »

Richtig. Es sind die Zeilen 421 und 422.

Damit der Callmonitor funktioniert, muss an der Fritzbox der Port 1012 geöffnet sein. Dieser Port kann über ein angeschlossenes Telefon mit dem Code #96*5* geöffnet und mit #96*4* wieder geschlossen werden.
Gruß Garfield

Socke
Lazarusforum e. V.
Beiträge: 3178
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: Event in Class ausführen.

Beitrag von Socke »

Garfield hat geschrieben:Dieser Port kann über ein angeschlossenes Telefon mit dem Code #96*5* geöffnet und mit #96*4* wieder geschlossen werden.
Internes Telefonbuch und über den Browser anrufen :D

Jetzt werden die korrekten Verbindungsdaten angezeigt; die Methode TCallMonitor.DoCallEvent() wird aber immer noch nicht aufgerufen.
Dateianhänge
callmonitor_anzeige.png
callmonitor_anzeige.png (3.74 KiB) 2211 mal betrachtet
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

Benutzeravatar
Garfield
Beiträge: 173
Registriert: Do 5. Jun 2008, 22:07
OS, Lazarus, FPC: Ubuntu 22.04 LTS (Laz 3.0 FPC3.2.2)
CPU-Target: 64Bit
Wohnort: Aken

Re: Event in Class ausführen.

Beitrag von Garfield »

Ja Socke, das ist normal. Der Client "lauscht" jetzt auf Ereignisse an der Fritzbox. Heißt, es wartet auf eingehende und ausgehende Anrufe. Siehe auch diese Seite. Meine Frau war heute auch schon ein wenig genervt, weil ich andauernd angeklingelt habe.
Gruß Garfield

Socke
Lazarusforum e. V.
Beiträge: 3178
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: Event in Class ausführen.

Beitrag von Socke »

Garfield hat geschrieben:Ja Socke, das ist normal. Der Client "lauscht" jetzt auf Ereignisse an der Fritzbox. Heißt, es wartet auf eingehende und ausgehende Anrufe.
Okay, dann sind damit meine Testmöglichkeiten erschöpft. Die Fritzbox ist hier nämlich reiner Internet-Provider. Telefon wird anders verarbeitet.

Also zurück zur Theorie: Wenn Assignd(fOnCallEvent) eine SIGSEGV auslöst, kann das nur heißen, dass auf fOnCallEvent nicht zugegriffen werden kann. Überprüfe mal, was bei Assigned(Self) und Self is TObject herauskommt oder was der Debugger ausspuckt.
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

Benutzeravatar
Garfield
Beiträge: 173
Registriert: Do 5. Jun 2008, 22:07
OS, Lazarus, FPC: Ubuntu 22.04 LTS (Laz 3.0 FPC3.2.2)
CPU-Target: 64Bit
Wohnort: Aken

Re: Event in Class ausführen.

Beitrag von Garfield »

Bei mir geht alles über die Fritzbox.

Assigned(Self) funktioniert.

Code: Alles auswählen

protected
    procedure DoCallEvent(aBuffer: PAnsiChar; aDataLen: Integer);
Könnte es daran liegen, dass die procedure unter protected steht?
Dateianhänge
Bild1.png
Bild1.png (3.72 KiB) 2207 mal betrachtet
Bild.png
Gruß Garfield

Benutzeravatar
Garfield
Beiträge: 173
Registriert: Do 5. Jun 2008, 22:07
OS, Lazarus, FPC: Ubuntu 22.04 LTS (Laz 3.0 FPC3.2.2)
CPU-Target: 64Bit
Wohnort: Aken

Re: Event in Class ausführen.

Beitrag von Garfield »

Assigned(Self) funktioniert. Wenn ich dies direct vor Assigned(fOnCallEvent) stelle, bekomme ich dieses Fenster.
Dateianhänge
Bild3.png
Bild3.png (4.44 KiB) 2207 mal betrachtet
Bild2.png
Bild2.png (3.27 KiB) 2207 mal betrachtet
Gruß Garfield

Benutzeravatar
Garfield
Beiträge: 173
Registriert: Do 5. Jun 2008, 22:07
OS, Lazarus, FPC: Ubuntu 22.04 LTS (Laz 3.0 FPC3.2.2)
CPU-Target: 64Bit
Wohnort: Aken

Re: Event in Class ausführen.

Beitrag von Garfield »

Wenn ich die procedure DoCallEvent aus protected herausnehme und unter private setze, gibt es den Fehler nicht mehr und im Hint steht fOnCallEvent = 0 und Assigned(fOnCallEvent) ist False.
Gruß Garfield

Socke
Lazarusforum e. V.
Beiträge: 3178
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: Event in Class ausführen.

Beitrag von Socke »

Garfield hat geschrieben:Assigned(Self) funktioniert. Wenn ich dies direct vor Assigned(fOnCallEvent) stelle, bekomme ich dieses Fenster.
Die Adresse 0x72 (114 dezimal) sieht denkwürdig aus. Das sieht so aus, als ob an einer Stelle im Speicher zu weit geschrieben wird (ein Puffer wird zu klein dimensioniert, maximale Größe wird falsch übergeben, etc.).

Mir sind da nebenbei noch ein paar Detailfragen aufgetaucht:
  • Die Klasse TCallMonitor wird von TThread abgeleitet; die Thread-Funktionalitäten werden aber nicht genutzt; TThread.Execute wird nicht überschrieben (abstrakt!)
  • TCallMontior.Destroy ruft nicht TThread.Destroy auf
  • TCallMonitor.Destroy kann nicht ohne Exception TThread.Destroy aufrufen
  • Versuche mal den Puffer in TCallMonitor.WindowProc mit FillChar(aBuffer[0], Length(aBuffer) * Sizeof(aBuffer[0]), 0); zu überschreiben
  • Aktiviere in den Compiler-Optionen die unit heaptrc. Da kommt eine lange Liste von Speicherstellen heraus, die nicht wieder freigegeben werden sobald die Verbindung mit der Fritzbox hergestellt wurde.
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

Benutzeravatar
Garfield
Beiträge: 173
Registriert: Do 5. Jun 2008, 22:07
OS, Lazarus, FPC: Ubuntu 22.04 LTS (Laz 3.0 FPC3.2.2)
CPU-Target: 64Bit
Wohnort: Aken

Re: Event in Class ausführen.

Beitrag von Garfield »

Ich muss jezt erst mal eine kleine Pause machen.

Ich habe geändert:

-> TCallMonitor = Class
-> FillChar(aBuffer[0], Length(aBuffer) * Sizeof(aBuffer[0]), 0);
-> unit heaptrc aktiviert

Mein Haltepunkt liegt direkt vor dem Assigned(fOnCallEvent) und ich bekomme vorher eine Zugriffsverletzung. Muss nachher den Haltepunkt verlegen.
Gruß Garfield

Benutzeravatar
Garfield
Beiträge: 173
Registriert: Do 5. Jun 2008, 22:07
OS, Lazarus, FPC: Ubuntu 22.04 LTS (Laz 3.0 FPC3.2.2)
CPU-Target: 64Bit
Wohnort: Aken

Re: Event in Class ausführen.

Beitrag von Garfield »

Jetzt habe ich wieder den Fehler "External SIGSEGV" und muss für heute Schluss machen. Meine Frau will jetzt Ruhe haben nach etwa 70mal anklingeln.
Gruß Garfield

Antrepolit
Beiträge: 340
Registriert: Di 12. Sep 2006, 08:57
OS, Lazarus, FPC: Winux (L 0.9.xy FPC 2.2.z)
CPU-Target: xxBit
Kontaktdaten:

Re: Event in Class ausführen.

Beitrag von Antrepolit »

Code: Alles auswählen

{$IFDEF DELPHI}
    TOnCallEvent = procedure(Sender: TObject; aCall: TCallEventData; aMsg: AnsiString) of object;
  {$ELSE}
    TOnCallEvent = procedure(aCall: TCallEventData; aMsg: AnsiString) of object;
  {$ENDIF}
Und in deinem Projekt steht

Code: Alles auswählen

{$IFDEF DELPHI}
program Callmonitor;
 
{$mode objfpc}{$H+}
Wie wäre es mit einem {$mode delphi} und den entsprechenden Anpassungen? Desweiteren würde ich die Integer-Variablen in der Datenstruktur TCallEventData als 32-Bit-Integer explizit deklarieren. Bzw. wie groß sind denn die zurückgelieferten integer-Werte tatsächlich?

Und was soll diese Sektion? Das müsste mal angepasst werden, sieht etwas obsolet aus. Da musst du noch einige Funktionen adaptieren.

Code: Alles auswählen

{$IFDEF DELPHI}
    fHandle := AllocateHWnd(WindowProc);
  {$ELSE}
    fWndClass.style         := CS_HREDRAW or CS_VREDRAW;
    fWndClass.lpfnWndProc   := @DefWindowProc;
    fWndClass.cbClsExtra    := 0;
    fWndClass.cbWndExtra    := 0;
    fWndClass.hInstance     := HInstance;
    fWndClass.hIcon         := 0;
    fWndClass.hCursor       := 0;
    fWndClass.hbrBackground := 0;
    fWndClass.lpszMenuName  := NIL;
    fWndClass.lpszClassName := PAnsiChar('TCallMonitor');
    if RegisterClass(fWndClass) <> 0
    then fHandle := CreateWindowEx(WS_EX_TOOLWINDOW, PAnsiChar('TCallMonitor'), nil, WS_POPUP, 0, 0, 0, 0, 0, 0, HInstance, nil);
    if fHandle <> 0
    then SetWindowLong(fHandle, GWL_WNDPROC, PtrInt(@WindowProc));
  {$ENDIF}
Grüße, Antrepolit

care only if your os is really burning

Benutzeravatar
Garfield
Beiträge: 173
Registriert: Do 5. Jun 2008, 22:07
OS, Lazarus, FPC: Ubuntu 22.04 LTS (Laz 3.0 FPC3.2.2)
CPU-Target: 64Bit
Wohnort: Aken

Re: Event in Class ausführen.

Beitrag von Garfield »

Hallo Antrepolit,

anfangs hatte ich beim Event TOnCallEvent keine Unterscheidung nach Delphi und Lazarus. Dann habe ich gelesen, dass kein TObject übergeben werden muss und habe es für Lazarus weggelassen.

Eine Änderung von {$mode objfpc}{$H+} nach {$mode delphi} in uMain bringt anscheinend nur, dass ich das @ bei der Zuweisung des Events weglassen kann.

Das mit der fWndClass ist dafür, dass die WindowProc aufgerufen wird. Das hatte ich bereits im vergangenen Jahr eingebaut. Ob es veraltet ist, weiß ich nicht. Etwas anderes habe ich bisher nicht gefunden.
Gruß Garfield

Benutzeravatar
Garfield
Beiträge: 173
Registriert: Do 5. Jun 2008, 22:07
OS, Lazarus, FPC: Ubuntu 22.04 LTS (Laz 3.0 FPC3.2.2)
CPU-Target: 64Bit
Wohnort: Aken

Re: Event in Class ausführen.

Beitrag von Garfield »

Ich habe das Projekt wieder hervorgeholt und bei Turbodelphi nachgesehen. Nach einigen Stunden habe ich den Fehler endlich gefunden.

Da ein Handle benötigt wurde, wurde die Klasse registriert (RegisterClass), ein unsichtbares Fenster erstellt (CreateWindowEx) und die Adresse für die Nachrichtenschleife gesetzt (SetWindowLong). In Delphi gibt es dafür die function AllocateHwnd. In Lazarus ist sie zumindest bis Version 1.0 nicht implementiert bzw. enthält sie den Aufruf zum Auslösen des Lauflauffehlers 217. Deshalb hatte ich obige Zeilen selbst geschrieben. Beim Vergleichen mit Turbodelphi fand ich nun den Fehler. Beim Aufruf von SetWindowLong fehlte ein MakeObjectInstance.

Code: Alles auswählen

function AllocateHWnd(Name: String; WndProc: TWndMethod): HWND;
var
  fWndClass : TWndClass;
  aATOM     : TAtom;
begin
  Result := INVALID_HANDLE_VALUE;
  {
  *  Struktur zum Registrieren der Klasse füllen.
  }
  FillChar(fWndClass, SizeOf(TWndClass), 0);
  fWndClass.lpfnWndProc   := @DefWindowProc;
  fWndClass.hInstance     := HInstance;
  fWndClass.lpszClassName := PChar(Name);
  {
  *  Klasse registrieren
  }
  aAtom := Windows.RegisterClass(fWndClass);
  if aATOM <> 0
  then begin
    {
    *  Wenn die Klasse registriert wurde das Fenster erstellen.
    }
    Result := CreateWindowEx(WS_EX_TOOLWINDOW, fWndClass.lpszClassName, nil, WS_POPUP, 0, 0, 0, 0, 0, 0, fWndClass.HInstance, nil);
    {
    *  Wenn das Fenster erstellt wurde, die Nachrichtenschleife mit dem Fenster verbinden.
    }
    if (Result <> INVALID_HANDLE_VALUE) and Assigned(WndProc)
    then SetWindowLong(Result, GWL_WNDPROC, Longint(MakeObjectInstance(WndProc)));
  end;
end;
Vielleicht kann man sich die Übergabe des Klassennamens sparen wenn man wie Delphi GetClassInfo verwendet.
Gruß Garfield

Antworten