Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Ereignis wird nicht zu Ende geführt (früher "... ausgelöst") (https://www.delphipraxis.net/182740-ereignis-wird-nicht-zu-ende-gefuehrt-frueher-ausgeloest.html)

Schwedenbitter 13. Nov 2014 22:23


Ereignis wird nicht zu Ende geführt (früher "... ausgelöst")
 
Liste der Anhänge anzeigen (Anzahl: 2)
Hallo,

wie man dem beigefügtn Programmcode entnehmen kann, besteht mein Programmrumpf aus 3 Komponenten:
dem VCL-Hauptprogramm, einem Bild-Container und einem Thread, der die Bilder komprimieren soll (PNG und JPEG). Das ganze gibt es als Arbeitsentwurf auch "in groß" zum verarbeiten der Bilder direkt aus dem Scanner. Da das nicht funktioniert und ich nicht mit zig Zeilen Code nerven will, habe ich das ganze klein nachgebaut.

Beim Click auf Button1 wird ein Bild geladen, im Bild-Container (dieser wiederum in einer Liste) abgelegt und an einen Thread zum Rechnen übergeben. Wenn der Thread fertig ist, löst er ein Ereignis aus. In der entsprechenden Procedure wollte ich die Bilder speichern. Beim Speichern wiederum hängt er sich auf. Der gesamte Code ist als zip-Datei (mit und ohne Bild)angehängt. Der Code im Event selbst sieht wie folgt aus:
Delphi-Quellcode:
procedure TForm1.ThreadEnde(ID: Integer);
begin
   Memo1.Lines.Append('Event for ID ' + IntToStr(ID) + ' received');
   if ID > 2 then
   begin
      TMyObject(fObjectList[ID]).PNGStream.SaveToFile('bild.png');
      TMyObject(fObjectList[ID]).JPGStream.SaveToFile('bild.jpg');
   end;
   // Bei ID < 3 läuft alles durch. Es muss also Probleme beim Speichern geben.
   Button1.Enabled:=true;
   Memo1.Lines.Append('... all done - object ID is ' + IntToStr(ID));
end;
Kann mir jemand sagen, was das Problem ist und wie man es löst?

Gruß, Alex

[edit]
Sowas von schief gegangen!
Ich habe weiter getestet und gleichzeitig hier geschrieben. Sehe erst jetzt, dass das Thema nicht (mehr) passt. Das Ereignis wird ausgelöst, dann aber nicht weiter abgearbeitet. Evtl. kann das bitte ein Moderatoer oder so für mich ändern. Danke
[/edit]

Uwe Raabe 13. Nov 2014 22:43

AW: Ereignis wird nicht ausgelöst
 
Funktioniert doch...

Edit: Korrigiere - man muss dreimal auf den Button clicken damit es kracht...

Edit: Du übergibst TMyObject(fObjectList[Index]) an den Thread, aber Index ist nicht gleich ID! Beim Event greifst du aber über ID in die fObjectList und das gibt ein List index out of range.

Sir Rufo 13. Nov 2014 22:54

AW: Ereignis wird nicht ausgelöst
 
Obligatorische Frage bei Threads:

In welchem Thread-Kontext wird die Methode aufgerufen?

himitsu 13. Nov 2014 23:01

AW: Ereignis wird nicht ausgelöst
 
Antwort 1:
Delphi-Quellcode:
Synchronize(FireOnEnde);


Obligatorische Frage 2:
Sind die Komponenten auch alle thradsave? (erlaube Zugriffe in unterschiedlichen Threads)

Obligatorische Frage 3:
Relative Pfade ... Warum?



Zitat:

Zitat von Schwedenbitter (Beitrag 1279720)
Evtl. kann das bitte ein Moderatoer oder so für mich ändern.

Meinst du de Threadüberschrift?

Erster Beitrag -> Bearbeiten -> Erweiterter Editor -> Ändern -> Speichern

himitsu 13. Nov 2014 23:17

AW: Ereignis wird nicht ausgelöst
 
Warum wurde der Debugger nicht benutzt, oder hat der dir wirklich nicht den Grund genannt?
Zitat:

Erste Gelegenheit für Exception bei $7534C42D. Exception-Klasse EListError mit Meldung 'Listenindex überschreitet das Maximum (3)'. Prozess ObjectInThread.exe (162412)
In Zeile
Delphi-Quellcode:
Synchronize(FireOnEnde);
. (Fehlerzeile im Debugger durch Synchronize verfälscht, aber mit Haltepunkt in ThreadEnde wäre man auch so an die Richtige Zeile gekommen, wenn man das nicht von selber erkennt "3 Mal klicken ... 0-2 ... ahhhhhh")

Das Programm zeigt die Exception natürlich nicht an, weil DU nicht die Exceptions im Thread behandelst.
Die VCL zeigt nur Exceptions im Hauptthread an und da auch nur die, welche nicht via Synchronize in andere Threads umgeleitet werden.

Grund, warum es beim 3. Klick knallt:
Delphi-Quellcode:
if ID > 2 then
:roll:
Zitat:

// Bei ID < 3 läuft alles durch. Es muss also Probleme beim Speichern geben.
Also, der erste Satz wurde gut erkannt, aber die Schlussfolgerung ist natürlich falsch, was der Debugger einem aber zeigen täte,
denn es knallt schon vor dem Speichern, beim Zugriff auf die Liste.

Zitat:

Delphi-Quellcode:
Inc(fObjectID);
lMyObject:=TMyObject.create(fObjectID);
Index:=fObjectList.Add(lMyObject);

Wieso bekommt das TMyObject
Delphi-Quellcode:
eine andere ID
einen anderen Index, als dann in ThreadEnde benutzt wird?

1: fObjectID=1 und Index=0 -> übersprungen
2: fObjectID=2 und Index=1 -> übersprungen
3: fObjectID=3 und Index=2 -> ID=Index 3 gibt es nicht -> *peng


Und wehe du kommst auf die Idee ID-1 zu rechnen.
Man benutzt keine unabhängigen/unsynchronisierten Zähler für eine gemeinsame Sache :warn:

Schwedenbitter 13. Nov 2014 23:50

AW: Ereignis wird nicht ausgelöst
 
Danke für Eure Antworten!
Das Problem ist jetzt so gelöst:
Delphi-Quellcode:
procedure TForm1.ThreadEnde(ID: Integer);
Var
   I: Integer;
begin
   I:=0;
   while (I < fObjectList.Count) do
   begin
      if (TMyObject(fObjectList[I]).ID = ID) then break;
      Inc(I);
   end;
   if (I < fObjectList.Count) then // Wäre so, wenn ID nicht gefunden würde.
   begin
      TMyObject(fObjectList[I]).PNGStream.SaveToFile('bild.png');
      TMyObject(fObjectList[I]).JPGStream.SaveToFile('bild.jpg');
      Memo1.Lines.Append('... all done - object ID is ' + IntToStr(ID));
   end
   else Memo1.Lines.Append('ERROR: ID ' + IntToStr(ID) + ' not found!');
   Button1.Enabled:=true;
end;
Hintergrund der IDs hatte ich versucht zu erklären. Es kommen so ca. 50 Seiten/min. vom Scanner. Das soll in etwa auch so bleiben. Ich habe daher (neben dem Haupt-Thread der VCL) einen Thread gebaut, der in
Delphi-Quellcode:
tpIdle
rumdümpelt und nichts anderes tut, als eine Liste mit den Bildcontainern zu verwalten. Er sorgt auch für einen geordneten Ablauf der Kompression, Kontrastanpassung etc. pp.
Die Benutzer können aber - weil Thread - die Bilder schon während des Scannens aussortieren (=löschen). Das bringt dann den Index der Liste durcheinander und da habe ich mir eben über die ID geholfen. Dass es im ursprünglichen Code beim 3. Mal "knallt", war dem Umstand geschuldet, dass ich das
Delphi-Quellcode:
TMemoryStream.Save();
als Fehlerquelle ausgemacht hatte und grundsätzlich erstmal sehen wollte, ob die Procedure überhaupt bis zum Ende durchläuft...

Stichwort threadsichere Komponenten. Was versteht man darunter und wie finde ich es raus. Ich dachte, mit CriticalSections zu arbeiten, würde bzgl. Threads schon ausreichend sein. Da der HauptThread die Kommandos für das Packen verwaltet und den Container entsprechend mit einem TAG versieht, sollte also alles im Lot sein.

himitsu 14. Nov 2014 07:38

AW: Ereignis wird nicht zu Ende geführt (früher "... ausgelöst")
 
Weußt du, wie man 79% des Codes einsparen kann?

ThreadEnde bekommt das Objekt und nicht die ID, als Parameter. :stupid:

Schwedenbitter 14. Nov 2014 08:26

AW: Ereignis wird nicht zu Ende geführt (früher "... ausgelöst")
 
Zitat:

Zitat von himitsu (Beitrag 1279749)
Weußt du, wie man 79% des Codes einsparen kann?

ThreadEnde bekommt das Objekt und nicht die ID, als Parameter. :stupid:

Hast Du das durchgerechnet? :lol:
Spaß beiseite: Würde es Dir viel ausmachen, mir das vom Grundsatz zu skizzieren?

Ich baue immer noch an dem großen Thread - das hier ist wie gesagt nur ein Ausschnitt - und optimiere den weiter. Das würde ich weiter nach hinten verschieben, wenn Du mir das erklärtest...

Uwe Raabe 14. Nov 2014 08:36

AW: Ereignis wird nicht zu Ende geführt (früher "... ausgelöst")
 
Zitat:

Zitat von Schwedenbitter (Beitrag 1279754)
Würde es Dir viel ausmachen, mir das vom Grundsatz zu skizzieren?

Na, das ist dich ziemlich ersichtlich. Wenn du als Parameter für ThreadEnde nicht die ID sondern z.B. TheObject: TMyObject deklarierst,

Delphi-Quellcode:
procedure TForm1.ThreadEnde(TheObject: TMyObject);
begin
  Memo1.Lines.Append('Event for ID ' + IntToStr(TheObject.ID) + ' received');
  if TheObject.ID > 2 then
  begin
    TheObject.PNGStream.SaveToFile('bild.png');
    TheObject.JPGStream.SaveToFile('bild.jpg');
  end;
  // Bei ID < 3 läuft alles durch. Es muss also Probleme beim Speichern geben.
  Button1.Enabled:=true;
  Memo1.Lines.Append('... all done - object ID is ' + IntToStr(TheObject.ID));
end;
und dann in FireOnEnde sowas
Delphi-Quellcode:
if assigned(fOnEnde) then fOnEnde(fMyObject);

schreibst, dann kannst du den Zugriff über die Liste sparen.

Schwedenbitter 14. Nov 2014 16:53

AW: Ereignis wird nicht zu Ende geführt (früher "... ausgelöst")
 
Liste der Anhänge anzeigen (Anzahl: 1)
Zitat:

Zitat von Uwe Raabe (Beitrag 1279756)
...
schreibst, dann kannst du den Zugriff über die Liste sparen.

Danke erstmal für diese ausführlich Erklärung. Ich dachte wirklich, er meint 79% des gesamten Programm-Codes.

Ich muss jetzt doch mal den Code der gesamten Objekte hier einbringen. Konkret geht es um die Zeilen 242/243 bzw. 255/256 in der Unit _PictureStack.pas. Ich habe - auch in der abgespeckten Testversion - nie Probleme gehabt, dem Thread ein Event mit auf den Weg zu geben, über das er sein Ende mitteilen soll.
Hier meckert der Compiler aber rum:
Zitat:

[dcc32 Fehler] _PictureStack.pas(243): E2009 Inkompatible Typen: 'Reguläre Prozedur und Methodenzeiger'
Nachdem ich das ganze jetzt schon zum 3. Mal komplett neu aufgebaut und wieder keinen Fehler im Aufbau gesehen habe, muss ihn ein anderer finden. Auch der Hilfetext ist nicht wirklich hilfreich :roll:

Um die Problematik der Rückgabe des ganzen Bild-Container, würde ich mich danach kümmern wollen...


Alle Zeitangaben in WEZ +1. Es ist jetzt 18:08 Uhr.
Seite 1 von 2  1 2      

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