Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Access Violation durch Length(Array)? (https://www.delphipraxis.net/171855-access-violation-durch-length-array.html)

Jumpy 28. Nov 2012 15:36

Delphi-Version: 5

Access Violation durch Length(Array)?
 
Liste der Anhänge anzeigen (Anzahl: 1)
Bei obiger Funktion kommt es immer mal wieder zu einer Fehlermeldung (siehe Bild). Habs auch schon mit High(priv_Calls)+1 versucht. Ich vermute immer dann, wenn priv_Calls keine Elemente (mehr) hat, aber kann das sein? Warum liefert Length nicht einfach 0 und gut ist?

Delphi-Quellcode:
function TTapiLine.CallCount: integer;
begin
  Result:=0;
  try
    Result:=Length(priv_Calls);
  except on e:Exception do
    showmessage(e.message);
  end;
end;

//priv_Calls: TTapiCalls;
//TTapiCalls = Array of TTapiCall;
//TTapiCall ein(e) Objekt/Klasse
Ohne den try...except Block, also nur mit
Code:
Result:=Length(priv_Calls);
kommt die Fehlermeldung nur einmal, mit dem Block kommt es irgendwie zu einer Endlosschleife und die Fehlermeldung kommt immer wieder.

DeddyH 28. Nov 2012 15:40

AW: Access Violation durch Length(Array)?
 
Wie ist es mit
Delphi-Quellcode:
function TTapiLine.CallCount: integer;
begin
  if Assigned(priv_Calls) then
    Result:=Length(priv_Calls)
  else
    Result:=0;
end;
:?:
Möglicherweise ist das Array ja bereits aufgeräumt worden, das kann man ja mit SetLength oder mit Setzen auf nil bewerkstelligen.

[edit] Obwohl: auch da müsste doch Length eigentlich 0 sein, wenn ich mich nicht irre. [/edit]

himitsu 28. Nov 2012 15:50

AW: Access Violation durch Length(Array)?
 
Assigned hilft eigentlich nichts, denn genau das Selbe macht Length intern auch. (is praktisch nur doppeltgemoppelt)
[edit] Jupp, "leer" = nil.

Was ist priv_Calls? (übersehn :oops:)
Und was machst du damit? (wie wird es z.B. befüllt)



Nja, wenn ich raten müßte (anhand der wenigen vorhandenen Informationen), dann würde ich erstmal an einen Buffer-Overrun denken, mit dem du dir dein Array zerschossen hast.
:glaskugel:

DeddyH 28. Nov 2012 15:51

AW: Access Violation durch Length(Array)?
 
Lesen:
Zitat:

Delphi-Quellcode:
//priv_Calls: TTapiCalls;
//TTapiCalls = Array of TTapiCall;
//TTapiCall ein(e) Objekt/Klasse


Jumpy 28. Nov 2012 16:09

AW: Access Violation durch Length(Array)?
 
Hier mal wie das Array befüllt und geleert wird. Mag sein, dass die Remove-Methode noch etwas umständlich ist, die hab ich noch nicht überarbeitet, vllt. wird da auch das Array zerschossen?

Delphi-Quellcode:
procedure TTapiLine.AddCall(Call: TTapiCall);
begin
  SetLength(priv_Calls,High(priv_Calls)+2);
  priv_Calls[High(priv_Calls)]:=Call;
end;

procedure TTapiLine.AddCall(CallHandle: Cardinal);
var Call:TTapiCall;
begin
  Call:=TTapiCall.Create(CallHandle);
  if Call.Initialized then
    AddCall(Call)
  else
    Call.Free;
end;

procedure TTapiLine.RemoveCall(CallHandle: Cardinal);
var i,j:integer;
begin
  if CallCount=0 then exit;
  i:=low(priv_calls);
  while (priv_calls[i].Handle<>CallHandle) and (i<high(priv_calls)) do
    inc(i);
  if priv_calls[i].Handle=CallHandle then
    begin
    for j:=i to high(priv_Calls)-1 do
      priv_Calls[j]:=priv_Calls[j+1];
    Setlength(priv_Calls, High(priv_Calls));
    end;
end;

himitsu 28. Nov 2012 16:30

AW: Access Violation durch Length(Array)?
 
Direkt einen Fehler seh ich in deinen Funktionen eigentlich nicht.
Deine Schleife ist soweit sicher, aber im Notfalls darfst du gerne die Bereichsprüfung in den Projektoptionen aktivieren, welche dann die indize der Array-Zugriffe prüft, ob da nichts außerhalb des Speicherbereichs liegt.
(hätte ja sein können, daß du stattdessen einfach alles zusammen per Move verschiebst und nicht jeden Eintrag einzeln)

Das was dein Array zerschießt kann aber auch eine komplett andere Codezeile sein ... fremden Speicher darf man auch kaputtmachen und nicht nud den Eigenen.
Es kann natürlich auch sein, daß die TTapiLine-Instanz weg ist oder daß dur irgendwo nicht threadsave auf diese Funktionen zugreifst.


Delphi-Quellcode:
procedure TTapiLine.AddCall(Call: TTapiCall);
begin
  //Call schon im Array drin ist, dann hat dein Remove problemchen, weil es ja nur nach einem Eintrag sucht und nicht nach mehreren
  SetLength(priv_Calls, Length(priv_Calls)+1); // mit Length ist's hier "verständlicher", was gemacht wird
  priv_Calls[High(priv_Calls)]:=Call;
end;

procedure TTapiLine.AddCall(CallHandle: Cardinal);
var Call:TTapiCall;
begin
  // nur wenn Initialized keine Exceptions erzeugen kann, dann wird .Free sicher aufgerufen (Fehler im AddCall einfach mal ausgeschlossen)
  Call:=TTapiCall.Create(CallHandle);
  if Call.Initialized then
    AddCall(Call)
  else
    Call.Free;
end;

procedure TTapiLine.RemoveCall(CallHandle: Cardinal);
var i,j:integer;
begin
  // durch die umgedrehte und leicht veränderte While-Prüfung, konnte das erste IF entfallen und das Zweite vereinfacht werden,
  // da im While nur vorhandene und davon alle Einträge geprüft werden
  i := Low(priv_calls);
  while (i < Length(priv_calls)) and (priv_calls[i].Handle <> CallHandle) do // oder (i <= High(priv_calls))
    inc(i);
  if i < Length(priv_calls) then
    begin
      for j := i+1 to High(priv_Calls) do
        priv_Calls[j-1] := priv_Calls[j]; // sprechender Code ... -1 = 1 vorkopieren (OK, das alte +1 = von dahinter herholen geht notfalls och)
      Setlength(priv_Calls, Length(priv_Calls)-1); // wie oben
    end;
end;

shmia 28. Nov 2012 17:42

AW: Access Violation durch Length(Array)?
 
Höchstwahrscheinlich operierst du auf einem Nil-Objekt rum.
Das Problem ist nicht, dass priv_Calls = Nil sein könnte sondern dass self = Nil ist.
Delphi-Quellcode:
function TTapiLine.CallCount: integer;
begin
  // diese Sicherheitsabfrage schlägt Alarm bevor Schlimmeres passiert
  // Assertions müssen in den Projektoptionen natürlich zugelassen sein
  Assert(Assigned(self));

  Result:=0;
  try
    Result:=Length(priv_Calls);
  except on e:Exception do
    showmessage(e.message);
  end;
end;

Bjoerk 28. Nov 2012 17:50

AW: Access Violation durch Length(Array)?
 
Zitat:

Zitat von Jumpy (Beitrag 1193457)
Hier mal wie das Array befüllt und geleert wird. Mag sein, dass die Remove-Methode noch etwas umständlich ist, die hab ich noch nicht überarbeitet, vllt. wird da auch das Array zerschossen?

Delphi-Quellcode:
procedure TTapiLine.AddCall(Call: TTapiCall);
begin
  SetLength(priv_Calls,High(priv_Calls)+2); // ***
  priv_Calls[High(priv_Calls)]:=Call;
end;

Wenn ich's noch recht in Erinnerung habe, liefert High für Length = 0 Minus Eins zurück.

himitsu 28. Nov 2012 18:35

AW: Access Violation durch Length(Array)?
 
High = Length - 1
da dynamische Arrays immer bei Index 0 beginnen.

Jumpy 29. Nov 2012 09:21

AW: Access Violation durch Length(Array)?
 
Edit: Fehler gefunden. Diesen Post gerne überspringen und unten weiterlesen!

Hallo und guten Morgen und danke für die Hilfe gestern.

Eine Nacht drüber geschlafen brachte immer noch keine Lösung.
Hab's so geändert, das bei AddCall nun eine globale Zählervariable hochgezählt wird, bei removeCall wird diese wieder runtergesetzt und die Funktion CallCount liest diese nun nur noch aus:

Delphi-Quellcode:
function TTapiLine.CallCount: integer;
begin
{  Result:=0;
  try
    Result:=Length(priv_Calls);
  except
  end;}
  Assert(Assigned(self));
  try
    Result:=priv_CallCount;
  except on e:exception do
    begin
    showmessage(IntToStr(priv_LineNumber));
    end;
  end;
end;
Trotzdem kommt noch der Fehler. Zudem kommt der dann nochmal bei dem showmessage (womit ich sehen wollte, welche Line den Fehler verursacht, was aber nicht klappt). Dies legt den Verdacht nahe, das shmia vllt. die richtige Idee hatte, aber das Assert macht nichts? In den Debug-Optionen ist es aktiviert (überlauf usw. sind deaktiviert).

Zum Fehler nochmal: Er tritt scheinbar immer nur 1 mal auf 2-5 Sekunden nach Programmstart, danach (hab's mal 30 min laufen lassen) nicht mehr.
Vllt. ist da wirklich am Anfang irgendeine "Geisterleitung" die mir hier in den Kram spukt. Das ganze Programm wird durch Tapi-Aufrufe einer Callback-Funktion gesteuert. Mag sein, dass diese schon zu früh mal feuert, wenn noch nicht alle Leitungen stehen oder so. Muss ich mal weiter experimentieren. Andere Ideen aber weiterhin willkommen :-D

Jumpy 29. Nov 2012 09:57

AW: Access Violation durch Length(Array)?
 
Den Fehler gefunden. Eine Callback-Funktion gibt immer den Handle des Calls weiter an eine Funktion, die den Callback Verarbeitet. Aus dem Handle wird der Handle der Line bestimmt, auf der der Call läuft und aus dem Linehandle wird der Index der Line im Array priv_Lines bestimmt.

Aber nun: Bei einer bestimmten Callback-Nahricht wird nicht der Handle des Calls, sondern der einer Line zurückgegeben. Die Funktion, die aus dem Call-Handle einen Line-Handle sucht wird daher natürlich nicht fündig und liefert 0 zurück.

Die Funktion, die aus dem LineHandle nun den Index sucht findet damit natürlich auch nix und liefert -1 zurück.

Dann wird auf priv_Lines[-1].CallCount zugegriffen (weil bisher der Test Index>=0 fehlt) und da knallt es. Keine Ahnung warum es da innerhalb der CallCount knallt und nicht in der Funktion in der die Zeile priv_Lines[-1].CallCount steht und wo doch ein Idndex out of Bounds oder so gemeldet werden müsste?

himitsu 29. Nov 2012 12:53

AW: Access Violation durch Length(Array)?
 
Zitat:

Zitat von Jumpy (Beitrag 1193535)
Keine Ahnung warum es da innerhalb der CallCount knallt und nicht in der Funktion in der die Zeile

Das kommt eben immer drauf an, wie und auf was man zugreift.

Bei einem Array kann man locker vor den Ersten oder hinter den letzten eintrag zugreifen.
es kommt nur drauf an, was für einen Typ die Felder haben (Integer ist unproblematischer als z.B. String) und ob an der Stelle zufällig etwas anderes im Speicher liegt, so daß keine Zugriffsverletzung auftritt.

Bezüglich der Arrays kann man das ganz leicht prüfen lassen, indem man z.B. die Bereichsprüfung in den Projektoptionen aktiviert.

Bjoerk 29. Nov 2012 15:13

AW: Access Violation durch Length(Array)?
 
Statt des Arrays würde ich eine TList verwenden.

Jumpy 29. Nov 2012 16:35

AW: Access Violation durch Length(Array)?
 
Wenn ich schon Generics könnte würd ich das machen, aber ohne ist mir die ganze is/as-Casterei zu lästig (müsste bei meiner momentanen Coderenovierung zuviel anpacken).

Bjoerk 29. Nov 2012 22:55

AW: Access Violation durch Length(Array)?
 
Zitat:

Zitat von Jumpy (Beitrag 1193607)
[..] aber ohne ist mir die ganze is/as-Casterei zu lästig (müsste bei meiner momentanen Coderenovierung zuviel anpacken).

Du meinst TObjectList? Bei einer TList ist das so nicht erforderlich. Ich hab' mal auf die Schnelle was zusammen gezimmert. Ich will Dir das nicht aufzuschwatzen, aber das sollte doch in deine App mit vergleichsweise wenig Aufwand einzubauen sein? Der Vorteil wäre eine saubere Speicherverwaltung und die leichte Handhabbarkeit. Satt des Array's halt eine Instanz der Klasse verwenden.

Delphi-Quellcode:
unit uCallItems;

interface

uses
  Classes, TTAPICallunits;

type
  TCallItem = class
  private
    FCall: TTApiCall;
    FHandle: Cardinal;
  public
    property Call: TTApiCall read FCall;
    property Handle: Cardinal read FHandle;
    constructor Create(const AHandle: Cardinal);
    destructor Destroy; override;
  end;

  TCallItems = class(TList)
  private
    function GetItem(Index: integer): TCallItem;
    procedure AddItem(const AItem: TCallItem);
    procedure DelItem(Index: integer);
  public
    procedure AddCall(const AHandle: Cardinal);
    procedure DelCall(const AHandle: Cardinal);
    function IndexOfHandle(const AHandle: Cardinal): integer;
    destructor Destroy; override;
    property CallItems[Index: integer]: TCallItem read GetItem; default;
  end;

implementation

constructor TCallItem.Create(const AHandle: Cardinal);
begin
  inherited Create;
  FHandle := AHandle;
  FCall := TTApiCall.Create(FHandle);
end;

destructor TCallItem.Destroy;
begin
  FCall.Free;
  inherited Destroy;
end;

destructor TCallItems.Destroy;
begin
  while Count > 0 do
    DelItem(Count - 1);
  inherited Destroy;
end;

function TCallItems.GetItem(Index: integer): TCallItem;
var
  PItem: ^TCallItem;
begin
  PItem := Items[Index];
  Result := PItem^;
end;

procedure TCallItems.AddItem(const AItem: TCallItem);
var
  PItem: ^TCallItem;
begin
  New(PItem);
  PItem^ := AItem;
  Add(PItem);
end;

procedure TCallItems.DelItem(Index: integer);
var
  PItem: ^TCallItem;
begin
  PItem := Items[Index];
  PItem^.Free;
  Dispose(PItem);
  Delete(Index);
end;

function TCallItems.IndexOfHandle(const AHandle: Cardinal): integer;
var
  PItem: ^TCallItem;
  I: integer;
begin
  Result := -1;
  for I := 0 to Count - 1 do
  begin
    PItem := Items[I];
    if PItem^.Handle = AHandle then
      Result := I;
  end;
end;

procedure TCallItems.DelCall(const AHandle: Cardinal);
var
  Index: Integer;
begin
  Index := IndexOfHandle(AHandle);
  if Index > -1 then
    DelItem(Index);
end;

procedure TCallItems.AddCall(const AHandle: Cardinal);
var
  Item: TCallItem;
begin
  If IndexOfHandle(AHandle) = -1 then
  begin
    Item := TCallItem.Create(AHandle);
    AddItem(Item);
  end;
end;

end.

Jumpy 30. Nov 2012 09:02

AW: Access Violation durch Length(Array)?
 
Danke für die Mühe, das sieht tatsächlich so aus, als könnte ich das für meine Zwecke mit nur wenig Anpassung brauchen. Der Trick, auf den ich so nicht gekommen wäre, scheint zu sein, eine eigene Liste als Nachfahre von TList zu bauen und darin die casterei zu verstecken bzw. in deiner Version Pointerrei?
Ich hab normal kein Problem eigene Klassen zu erstellen, mit Vererbung oder Benutzung von anderen Klassenbeziehung. Aber irgendwie nehm ich immer die VCL als gegeben hin und komm nie drauf, das man da ja auch was Ableiten könnte oder das man mal eigene Komponenten machen könnte. Das höchste was ich da mal gemacht hab ist ein TMyFrame von dem dann andere Frames geerbt haben, so dass ich mir eine dynamische GUI bauen konnte.
Ich muss da mal die entsprechende Blockade im Kopf aufbohren und kreativer denken :coder:

Bjoerk 30. Nov 2012 09:55

AW: Access Violation durch Length(Array)?
 
Fein. :)

Noch schöner wär’s hier, wenn man aus AddCall / DelCall functions vom Typ boolean macht, dann kann man von außen ausführen:

Delphi-Quellcode:
if not FCallItems.AddCall(AHandle) then
  ShowMessage('Call konnte nicht hinzugefügt werden.');
Gruß
Thomas

DeddyH 30. Nov 2012 10:33

AW: Access Violation durch Length(Array)?
 
Könnte man dann nicht von TObjectList statt TList ableiten? Das wäre doch noch bequemer.

Bjoerk 30. Nov 2012 14:37

AW: Access Violation durch Length(Array)?
 
Und für noch Bequemere :)

http://www.delphi-treff.de/tutorials...ist/sonstiges/

Oft ist TItem aber vom Typ Record, da kommt man mit einer TObjectList nicht besonders weit...


Alle Zeitangaben in WEZ +1. Es ist jetzt 22:15 Uhr.

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