Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Seltsames Verhalten der Destruktoren (https://www.delphipraxis.net/194604-seltsames-verhalten-der-destruktoren.html)

Delbor 12. Dez 2017 11:43

Delphi-Version: XE8

Seltsames Verhalten der Destruktoren
 
Hi zusammen

In meiner gegenwärtigen Testanwendung - SynpdfTestProject - tritt plötzlich ein seltsames verhalten der Destuktoren auf. Ich muss allerdings zugeben, ich hab das erst jetzt, bei Auftreten des Fehlers, gecheckt.
Wenn ich die Anwendung beende, geschieht folgendes in der gezeigten Reiheenfolge:
Delphi-Quellcode:
procedure TSynpdfMain.FormDestroy(Sender: TObject);
begin
  Self.WriteReportFile;
  FReportList.Free;
end;
WriteReportFile schreibt den Inhalt der Reportliste im eine Datei. Da diese das jeweilige Datum und die Zeit im Namen trägt, wird sie jedesmal neu angelegt, und es tritt eine Exception (im Debugmodus) auf.. Eine normale 'Fehlermeldung' die schell weggeklickt ist.

Seit neuestem (heute) erhalte ich aber eine neue AV, und zwar hier:
Delphi-Quellcode:
destructor TPDFiumFrame.Destroy;
begin
  _Document := 0;
  FPages.Free;
  PDFPageClass.Free;
  FReportList.Free;
  inherited;
end;
Die einzige Änderung ist die Zeile mit der Freigabe der PDFPageClass, die ich vor Tagen schon hinzugefügt habe und die nie zu Problemen geführt hat. Ausgelöst wird der Fehler schon in der ersten Zeile bei

Delphi-Quellcode:
_Document := 0;

Der Vollständigkeit halber hier auch noch die beiden Constructoren:

Delphi-Quellcode:
procedure TSynpdfMain.FormCreate(Sender: TObject);
begin
  FReportList := TStringlist.Create;
  FReportList.Sorted := False;
  FReportList.Add('________________________________________');
  FReportList.Add('procedure TSynpdfMain.FormCreate;');
  FReportList.Add('________________________________________');
end;
und
Delphi-Quellcode:
constructor TPDFiumFrame.Create(AOwner: TComponent);
begin
{$IFDEF TRACK_EVENTS}
  AllocConsole;
{$ENDIF}
  inherited;
  ControlStyle := ControlStyle + [csOpaque];
  FZoom := 100;
  FPageIndex := -1;
  PDFPageClass := TPDFPageClass.Create; //<== meine zweite Änderung
  FSelBmp := TBitmap.Create;
  FSelBmp.Canvas.Brush.Color := RGB(50, 142, 254);
  FSelBmp.SetSize(100, 50);
  FPages := TList.Create;
  FReportList := TStringlist.Create; //<== meine erste Änderung
  FReportList.Sorted := False;
  FGetPageAt := 0;
  try
    FPDF_InitLibrary;
  except
    FStatus := TLabel.Create(Self);
    FStatus.Align := alClient;
    FStatus.Parent := Self;
    FStatus.Alignment := taCenter;
    FStatus.Layout := tlCenter;
    FStatus.Caption := sUnableToLoadPDFium;
  end;
end;
Weshalb jetzt plötzlich dieses Verhalten? In meiner Anwendung gibt es scon seit Wochen zwei Frameinstanzen, die jede vom Basisframe erbt und die beide von der Mainform zerstört wurden. Und damit auch ihr _Ducument - Handle (Das Handle des PDF-Dokumentes).

Gerade getestet und ebenso falsch:

Delphi-Quellcode:
  if _Document > 0 then
    _Document := 0;
Gruss
Delbor

Delphi.Narium 12. Dez 2017 11:52

AW: Seltsames Verhalten der Destruktoren
 
Wenn im Programmablauf TPDFiumFrame.Destroy vor TSynpdfMain.FormDestroy aufgerufen wird, existiert FReportList im FormDestroy nicht mehr und damit kann Self.WriteReportFile auch nicht mehr auf FReportList zugreifen, da ja bereits im TPDFiumFrame.Destroy freigegeben.

Ausserdem wird FReportList in TPDFiumFrame.Destroy und TSynpdfMain.FormDestroy freigegeben. Warum?

himitsu 12. Dez 2017 12:02

AW: Seltsames Verhalten der Destruktoren
 
Form.OnDestroy wurde "früher" im Inherited des Destructor ausgeführt.
> Form.OldCreateOrder=True oder ganz alten Delphis

Form.OnDestroy wird "aktuell" im BeforeDestruction ausgeführt.
> Form.OldCreateOrder=False

Beim Upgrade alter Formulare in neues Delphi steht dieses Property auf True.
Bei neu erstellten Formularen im neuen Delphi steht dieses Property auf False.

PS: Das Selbe gilt auch für OnCreate der Forms, also früher im Constructor und nun im AfterConstruction.


Fazit: Stell OldCreateOrder auf False (sollte im XE8 schon lange drin sein) und pass auf die Aufrufreihenfolge der Events aus.
> neu/aktuell : Create > OnCreate > ... > OnDestroy > Destroy
> früher : Create > ... > Destroy - und da dann jeweils im Inherited das OnCreate und OnDestroy, also vor oder nach dem eigenem Code
>> bei mir also fast immer OnCreate > Create > ... > Destroy > OnDestroy - da ich normalter Weise beim Erstellen das Inherited am Anfang und beim Freigeben am Ende stehen hab



"Doppelt" freigeben ist kein Problem, wenn man es richtig macht.
> Delphi-Referenz durchsuchenFreeAndNil

Aber grundsätzlich sollte man etwas auch dort freigeben, wo es auch erstellt wurde.
OnCreate > OnDesoroy
Constructor > Destructor

DeddyH 12. Dez 2017 12:04

AW: Seltsames Verhalten der Destruktoren
 
Außerdem sind das (hoffentlich) 2 verschiedene Instanzen, einmal ein Feld des Frames und einmal des Formulars.

Delbor 12. Dez 2017 12:15

AW: Seltsames Verhalten der Destruktoren
 
Hi Delphi.Narium

Zitat:

Wenn im Programmablauf TPDFiumFrame.Destroy vor TSynpdfMain.FormDestroy...
Es ist ja eben gerade umgekehrt. Es ist auch nicht die Liste, die die AV auslöst.

TPDFiumFrame.Destroy will das Dokumentenhandle auf 0 setzen. Da dieses aber schon mit den Frameinstanzen der Mainform zerstört wurde, gibts das nicht mehr - und das gibteine AV.

Zitat:

Ausserdem wird FReportList in TPDFiumFrame.Destroy und TSynpdfMain.FormDestroy freigegeben. Warum?
Anfänglich hatte ich diese Liste im Frame deklariert - dieser ist Opensource und stammt nicht von mir, und so wollte ich mir über die Abläufe klar werden.
Schliesslich deklarierte ich die Liste auch in der Mainform, um die Zusammenhänge Mainform.Frameinstanzen und Basisframe zu dokumentieren. Die Einträge des Basisframes erfolgen nun immer in die Liste der Mainform. Das Entfernen der Deklaration etc. aus dem Frame ging dabei schlicht vergessen.

Gruss
Delbor

Delbor 12. Dez 2017 12:38

AW: Seltsames Verhalten der Destruktoren
 
Hi zusammen

@DeddyH:
Zitat:

Außerdem sind das (hoffentlich) 2 verschiedene Instanzen, einmal ein Feld des Frames und einmal des Formulars.
Falls du die Reportliste meinst: ja, sind sie.

@ himitsu
Der Frame verfügt zumindest im OI über kein Property 'OLdCreateOrder', und dasjenige der Form steht auf false.

Zitat:

Aber grundsätzlich sollte man etwas auch dort freigeben, wo es auch erstellt wurde.
OnCreate > OnDesoroy
Constructor > Destructor
Wenn vorhanden, bzw. wenn es Sinn macht, benutze ich immer das OnCreate-Event, um etwas zu initialisieren. Frames haben kein (Published)-Event OnCreate, wesshalb hier sowas immer im Costructor/Destructor geschieht.
Wie oben schon erwähnt, ist der PDFiumFrame Opensource, und ich bin nicht sein Autor. Bis auf die Erwähnten Änderungen ist der Code also Original.

Gruss
Delbor

Ghostwalker 12. Dez 2017 12:58

AW: Seltsames Verhalten der Destruktoren
 
Wenn ich das richtig Verstehe, hast du eine Form sowie zwei Frames. Desweiteren eine globale Instanz eines Dokumentes.

Wenn das so richtig ist, ists logisch das es kracht. Es wird versucht das Dokument freizugeben, obwohls schon im 1. Frame freigegeben wurde.

Das zweite das mich ein wenig irritiert ist

Delphi-Quellcode:
  _Document := 0;
Hier wird nichts freigeben, sondern lediglich ein Handle auf 0 gesetzt. Das ursprüngliche Handle existiert aber nach wie vor. Im besten fall ist sowas ein Memoryleak. :)

hoika 12. Dez 2017 13:06

AW: Seltsames Verhalten der Destruktoren
 
Hallo,
was ist denn _Document für ein Typ?

Setze einen Breakpoint und mache mal einen Watchpoint auf Self.
Es könnte sein, dass das Frame bereits freigegeben ist.
_Document := 0; ist ja zufällig die erste Codezeile.
Drehe mal die ersten beiden Zeilen um, wo knallt es dann?

Delbor 12. Dez 2017 13:35

AW: Seltsames Verhalten der Destruktoren
 
Hi Ghostwalker

Zitat:

Wenn ich das richtig Verstehe, hast du eine Form sowie zwei Frames. Desweiteren eine globale Instanz eines Dokumentes.
Jein - es gibt nur einen Basisframe, davon aber zwei Frameinstanzen. Das nur mal, um 'alle Klarheiten zu beseitigen'.
Grundsätzlich existiert von einem (Basis-)Frame, derFrameklasse mit zugehöriger Unit, ja immer mindestens eine Frameinstanz. Zumindest dann, wenn der Frame auch verwendet werden soll.
Der Basisframe ist im diesem Fall TPDFiumFrame. In die Mainform eingefügt sind PDFiumFrame1 und PDFiumFrame2 als 2 Instanzen des TPDFiumFrames. Und diese beiden Instanzen werden mit der Mainform bei Programmende zerstört - und damit auch das gerbte Handle (bzw. die Instanz (Kopie) davon.

Das sind die Fehlermeldungen,, die ich in der Reihenfolge erhalte:

Zitat:

Benachrichtigung über Debugger-Exception
---------------------------
Im Projekt SynpdfTestProject.exe ist eine Exception der Klasse $C0000005 mit der Meldung 'access violation at 0x006476ae: read of address 0x000002c8' aufgetreten.
---------------------------
Zitat:

Benachrichtigung über Debugger-Exception
---------------------------
Im Projekt SynpdfTestProject.exe ist eine Exception der Klasse $C0000005 mit der Meldung 'access violation at 0x005c7d73: read of address 0x00000010' aufgetreten.
---------------------------
Zitat:

Synpdftestproject
---------------------------
Zugriffsverletzung bei Adresse 005C7D73 in Modul 'SynpdfTestProject.exe'. Lesen von Adresse 00000010.
---------------------------

Zitat:

Benachrichtigung über Debugger-Exception
---------------------------
Im Projekt SynpdfTestProject.exe ist eine Exception der Klasse $C0000005 mit der Meldung 'access violation at 0x005c7d73: read of address 0x00000010' aufgetreten.
---------------------------
Wichtig: Diesa habe ich erst seit heute, ohne auch nur die kleinste Änderung vorgenommen zu haben. Insbesondere sind im Framedestruktor ausser den dokumentierten Änderungen keine solchen vorgenommen worden.

Gruss
Delbor

Delbor 12. Dez 2017 13:49

AW: Seltsames Verhalten der Destruktoren
 
Hi Hoika
Document ist so deklariert:

Delphi-Quellcode:
FDocument : HPDFDocument;
...
 property _Document: HPDFDocument read FDocument write SetDocument;
Das Umdrehen der Codezeilen hat keine Änderung bewirkt.

Gruss
Delbor

hoika 12. Dez 2017 13:51

AW: Seltsames Verhalten der Destruktoren
 
Hallo,
zeig mal bitte die Zeilen des Erstellens und der Freigabe der beiden Frames.

Hast kein VCS, um auf den alten Stand zurückzuspringen?

Ghostwalker 12. Dez 2017 13:56

AW: Seltsames Verhalten der Destruktoren
 
Mal ganz platt gefrag:

Kann es sein, das einer der Aufrufe:

Delphi-Quellcode:
  FPages.Free;
  PDFPageClass.Free;
  FReportList.Free;
noch auf _Document zugreift ?

Delbor 12. Dez 2017 20:12

AW: Seltsames Verhalten der Destruktoren
 
Hi zusammen

Sorry, wenn ich erst jetzt antworte - Hausmannspflichten haben gerufen, bzw. sich schon fast die Seele aus dem Leib geschrien...:)

Die beiden Frame-Instanzen werden nicht zur Laufzeit erstellt, sondern per Klick im OI auf Frames und nach Klick auf die Mainform/eine Panelgroupseite zw ein Panel ähnlich wie eine Kompoonwnte an der gewünschten Stelle angelegt. Beide Instanzen erben also nur den Constructor-Code des Basisframes:
Delphi-Quellcode:
constructor TPDFiumFrame.Create(AOwner: TComponent);
begin
{$IFDEF TRACK_EVENTS}
  AllocConsole;
{$ENDIF}
  inherited;
  ControlStyle := ControlStyle + [csOpaque];
  FZoom := 100;
  FPageIndex := -1;
  PDFPageClass := TPDFPageClass.Create;
  FSelBmp := TBitmap.Create;
  FSelBmp.Canvas.Brush.Color := RGB(50, 142, 254);
  FSelBmp.SetSize(100, 50);

  FPages := TList.Create;
  FReportList := TStringlist.Create;
  FReportList.Sorted := False;
  FGetPageAt := 0;
  try
    FPDF_InitLibrary;
  except
    FStatus := TLabel.Create(Self);
    FStatus.Align := alClient;
    FStatus.Parent := Self;
    FStatus.Alignment := taCenter;
    FStatus.Layout := tlCenter;
    FStatus.Caption := sUnableToLoadPDFium;
  end;
end;
Wie ich auch in andern Threads schon gesagt habe, geht es darum, 2 Frames, oder eben besser, 2 Frameinstanzen, die auf dem selben Basisframe beruhen, zu synchronisieren. Im Anhang mal ein Screenshot meiner Testanwendung: Links der kleine, schmale Frame zeigt mehrere Seiten des Dokumentes. Ein Klick auf solch eine (z.B. die 3.) soll nun diese Seite in der rechten, grösseren Frameinstanz anzeigen.
Nach längerer Zeit hab ichs dann geschafft, dass in PDFiumFrame1 für jede angeklickte Seite auch dren korrekter Index, bzw. die Seitennummer, angezeigt wurde:
Delphi-Quellcode:
procedure TSynpdfMain.PDFiumFrame1MouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);    
  var SP, FP,MousePos :TPoint; Index : Integer; Page : TPDFiumFrame.TPDFPageClass;
begin
  Memo1.Lines.Clear;
  Memo1.Lines.Add('------ PDFiumFrame1MouseUp ------');
//(1)
  MousePos.X:= X;
  MousePos.Y:= y;
  Page := PDFiumFrame1.PageAt[MousePos];
//(2)
  PDFiumFrame2.PageIndex := Page.Index;
//  PDFiumFrame2.PDFSelPage.Index := Page.Index;
  PDFiumFrame2.SetChoosePage(Page.Index);
end;
Obiger Code entstand in den letzten Tagen; an den mit //(1) bezeichneten Stellen befindet sich Code, der mir zum einen die Seitengrössen und Indexe in PDFiumFrame1 in ein Memo ausgibt und zum andern bei (2) den Index und den Klassennamen der angeclickten Seite.
Die Methode PDFiumFrame1.PageAt[MousePos]; ist eine vom Basisframe geerbte Funktion. Und Page schliesslich ist ein Property vom Typ TPDFPage des Basisframes. Letztere Klasse ist allerdings im Basisframe privat deklariert und somit eigentlich unsichtbar. Mit DeddyHs Hilfe hab ichs dann doch geschafft. Ein Ausschnitt aus der Deklaration des Basisframes :
Delphi-Quellcode:
    procedure SetPageIndex(Value : Integer);
  public
    { Déclarations publiques }
    type
      TPDFPageClass = TPDFPage;
  public
    PDFPageClass : TPDFPageClass;
     
    constructor Create(AOwner: TComponent); override;
Aus dem Constructor des Frames:

Delphi-Quellcode:
  FPageIndex := -1;
  PDFPageClass := TPDFPageClass.Create;
... und einer aus der Mainform:
Delphi-Quellcode:
procedure TSynpdfMain.PDFiumFrame1MouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);    
  var MousePos :TPoint; Index : Integer; Page : TPDFiumFrame.TPDFPageClass;
begin
...
...
  MousePos.X:= X;
  MousePos.Y:= y;
  Page := PDFiumFrame1.PageAt[MousePos];
Und schliesslich befindet sich die in TSynpdfMain.PDFiumFrame1MouseUp aufgerufene Methode PDFiumFrame2.SetChoosePage(Page.Index) ebenfalls im Basisframe und wird von diesem an seine Instanzen vererbt.:

Delphi-Quellcode:
procedure TPDFiumFrame.SetChoosePage(Value: Integer);
begin
  if Self.PageIndex <> Value then
    Self.PageIndex := Value;
  Self.LoadVisiblePages;
  Application.ProcessMessages;
end;
Die aufgerufene Methode LoadVisiblePages:

Delphi-Quellcode:
procedure TPDFiumFrame.LoadVisiblePages;
var
  Index, Z, Marge: Integer; Page : TPDFPage; LSelectedPage : TRectD;
  Top, Scale : Double; LClient, LRect : TRect;
begin
  FPageIndex := -1;
  FCurPage := nil;
  for Index := 0 to FPages.Count - 1 do
  begin
    Page := FPages[Index];
    if Page.Selection = nil then
      Dec(Page.Visible);
    else
      Page.Visible := 0;
  end;
  LClient := ClientRect;
  Top := 0;
  Z := 0;
  Marge := PAGE_MARGIN;           //const PAGE_MARGIN = 5; // pixel
  Scale := FZoom / 100 * Screen.PixelsPerInch / 72;
  for Index := 0 to FPageCount - 1 do
  begin                            
  // compute page position
    LRect.Top := Round(Top * Scale) + Marge - VertScrollBar.Position;
    LRect.Left := PAGE_MARGIN + Round((FTotalSize.cx - FPageSize[Index].cx) / 2 * Scale) - HorzScrollBar.Position;
    LRect.Width := Round(FPageSize[Index].cx * Scale);
    LRect.Height := Round(FPageSize[Index].cy * Scale);  
    if LRect.Width < LClient.Width - 2 * PAGE_MARGIN then
      LRect.Offset((LClient.Width - LRect.Width) div 2 - LRect.Left, 0);
  // visibility test
    if LRect.IntersectsWith(LClient) then
    begin
      if FPageIndex < 0 then
        FPageIndex := Index;
      Page := GetPage(Index);
      Page.Rect := LRect;
      Page.Visible := 1;
    end;
  // don't go below LClient area
    if LRect.Top > LClient.Bottom then
      Break;
  // next page top position
    Top := Top + FPageSize[Index].cy;
    Inc(Marge, PAGE_MARGIN);
  end;
  // release any page that was not visibile for the last 5 paint events
  for Index := FPages.Count - 1 downto 0 do
  begin
    Page := FPages[Index];
    if Page.Visible < -5 then
    begin
      Page.Free;
      FPages.Delete(Index);
    end;
  end;
end;
Eigentlich hatte ich gehofft, dass ich mit der Übergabe des in PDFiumFrame1 ermittelten Seitenindexes an PDFiumFrame2 hier so die entsprechende Seite anzeigen lassen könnte. Das funktioniert so aber nicht.
Vielmehr denke ich, dass hier die eigentliche Quelle des deerzeitigen Problems steckt. Zur Erinnerung:
Delphi-Quellcode:
procedure TSynpdfMain.PDFiumFrame1MouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);    
  var MousePos :TPoint; Index : Integer; Page : TPDFiumFrame.TPDFPageClass;
begin
...
...
  MousePos.X:= X;
  MousePos.Y:= y;
  Page := PDFiumFrame1.PageAt[MousePos];
Hier wird von PDFiumFrame1.PageAt eine Seite zurückgegeben. Deren Index wird in TPDFiumFrame.SetChoosePage dem Property PageIndex des Basisframes zugewiesen. Dmit verfügt der Basisframe über ein Seitenhandle mehr, als in der Liste Pages gespeichert sind. Diese Liste wird bei Programmende freigegeben - nicht aber die Seite, die sich (noch) nicht in der Liste befindet, weshalb das Dokument nicht freigegeben werden kann.
So zumindest meine bisherige Erklärung für das zur Zeit auftretende Problem. Der Basisframe verfügt über ein Boolean-Feld FReload. Dessen Verwendung:
Delphi-Quellcode:
procedure TPDFiumFrame.PaintWindow(DC: HDC);
var
  Index : Integer;
  Page : TPDFPage;
  Client: TRect;
  WHITE : HBrush;
  SelDC : HDC;
  Blend : TBlendFunction;
begin
  FInvalide := False;
...
// check visibility
  if FReload or (FPages.Count = 0) then   //<==
  begin
    LoadVisiblePages;
    PDFiumFramePaintWindowToMemo1;      
    FReload := False;
  end;
// page background
Diese Art der Programmierung mit dauerndem Laden/Entladen der Seitenhandles ist für mich sehr ungewöhnlich, weshalb ich alles andere als sicher bin, die Arbeitsweise des Programms wirklich verstanden zu haben.

Gruss
Delbor

Ghostwalker 13. Dez 2017 02:49

AW: Seltsames Verhalten der Destruktoren
 
Einen Wunderschönen Guten Morgen :)

Frame1 stellt also die Thumbnails der Seiten eines Dokumentes dar (als Liste sozusagen) während Frame2 die ausgewählte Seite in einer großen Ansicht präsentiert.

D.h. beide Frames arbeiten mit dem gleichen Dokument und damit mit dem gleichen Dokumentenhandel.

Beim schließen des Dokumentes passiert nun folgendes:

Frame2.Destructor räumt sich auf und gibt das Handel vom Dokument frei.

danach

Frame1.Destructor räumt sich auf und versucht das Handel des Dokumentes frei zu geben.

Da Frame2 das schon gemacht hat, gibts eine AV.

Sinnvoller wäre es, das Dokument in der Form zu erzeugen und wieder frei zu geben und das ganze dann an die Frames zu übergeben.

Delbor 13. Dez 2017 08:11

AW: Seltsames Verhalten der Destruktoren
 
Hi Ghostwalker

Dir auch einen guten Morgen!

Zitat:

Beim schließen des Dokumentes passiert nun folgendes:
Frame2.Destructor räumt sich auf und gibt das Handel vom Dokument frei.

danach

Frame1.Destructor räumt sich auf und versucht das Handel des Dokumentes frei zu geben.
Da Frame2 das schon gemacht hat, gibts eine AV.
Sorry, wenn ich dir widerspreche! Bau dir doch mal ein kleines Testprogramm, ohne jede Komponente. Wenn du nun in der Toolleiste unter 'Standard<=Frames' dieses anklickst, dann auf die Form dahin klickst, wo du denn Frame haben willst, geschieht - nichts. Da zu dem Projekt noch kein Frame existiert, gibts auch keine Liste mit zur Verfügung steheden Frames, die dir angezeigt werden könnte.
Abhilfe schaffen 2 Möglichkeiten:
  • Du hast einen Frame, den du gerne in dem Projekt verwenden möchtest, der aber in einem völlig anderen Ordner liegen hast.
    • Öffne diesen Frame und speichere ihn unter dem gleichen Namen(wichtig!!!). aber natürlich nur den Dateinamen, in deinem Projektverzeichnis.
    • Dann klickst du auf <Projekt =>> Dem Projekt> hinzufügen und wählst nun die *.pas des Frames, um sie hinzuzufügen.
    • Nun kannst du ihn in der Toolpalette unter Frames auch auswählen.
  • Alternativ dazu kannst du unter >Datei =>> Neu =>> weitere> als neu zu erstellendes Objekt einen Frame wählen und erhälst nun einn leeres Feld, ähnlich wie bei einer Form
  • Diesen speicherst du wiederum im Projektorder. Dem Projekt hinzufügen musst du diesmal nicht - das hat Delphi schon gemacht

Nun hast du dem Projekt einen Basisframe hinzugefügt. Auf der Form hingegen befindet sich noch keine Frameinstanz. Nun setze im Projektverzeichnis einen Haltepunkt auf die Mainform - der soeben hinzugefügte Frame ist zwar in der uses-Liste vorhanden, nicht aber zwischen begin/end.
Wenn du nun das Projekt startest, wirst du bemerken, dass vor dem Erzeugen der Mainform der Basisframe erzeugt wird. Zumindest, wenn du diesem einen construktor himzugefügt hast und mit F7 durchstepst.
Bei Programmende laufen die Prozeduren in umgekehrter Reihenfolgee ab. Erst wird also die Form (mit ihren allfälligen Frameinstanzen) freigegeben und erst dann der Basisframe. Zusammen mit den Frameinstanzen werden auch deren Komponenten, Klassen und Handles freigegeben - _Document ist letzteres. Das _Document des Basisframes wird also erst mit diesem freigegeben (bzw. sollte vor dessen Zerstörung freigegeben werden. Aber da bin ich mir nicht sicher).

Vererbung erfolgt immer vom Basisframe zur Frameinstanz, umgekehrt jedoch nicht (wie das in Delphi überall der Fall ist). Wenn die Frameinstanzen der Form zerstört werden, werden also auch allfällige Handles dieser Instanzen zerstört, nicht aber das im Basisframe enthaltene Handle - das verzieht sich erst mit dem Basisframe ins Nirwana.

Wenn nun das Dokument 7 Seiten enthält und ich mir, wie oben ausgeführt, von der Prozedur GetPageAT eine Seite zurückgeben lasse, existieren erstmal acht Seiten. - zumindest in einer (oder auch beiden) Frameinstanzen. Dies wird in der Prozedur LoadVisiblePages bereinigt, wo alle nicht sichtbaren Seiten freigegeben werden. Anbei ein Ausschnitt aus PaintWindow:
Delphi-Quellcode:
// check visibility
  if FReload or (FPages.Count = 0) then
  begin
    LoadVisiblePages;
    PDFiumFramePaintWindowToMemo1;      
    FReload := False;
  end;
FReload muss hier also true sein, dass LoadVisiblePages aufgerufen wird.
So, wie ich den Programmablauf bisher verstanden habe, wird also ohne Aufraufe von PaintWindow und LoadVisiblePages und dem setzen von FReload auf True gar nichts neu gezeichnet, und es werden auch keine nichtsichtbaren Fenster gelöscht - auch deren Handles nicht.
Ich habe bislang noch nie mit Handles direkt gearbeitet, aber ich würde mich wohl schwer irren, wenn Windows ein Handle löschen (lassen) würde, das noch selbst ein gültiges Handle besitzt.

Ich weiss nicht, ob ich nun alles richtig verstanden habe, aber - weitermachen zeigt, was Sache ist.

Gruss
Delbor

PS:
Zitat:

Sinnvoller wäre es, das Dokument in der Form zu erzeugen und wieder frei zu geben und das ganze dann an die Frames zu übergeben.
Nein, in diesem Fall nicht. TPDFiumFrame ist in sich selbst voll funktionstüchtig. Zusammen mit der mitgelieferten Unit PDFium.Wrapper; gehörte er zu einem Beispielprojekt. Ich habe ihn hier in diese Anwendung auf die oben beschriebenen Weise importiert und die Wrapper-Unit unter diesem Projekt abgespeichert und hinzugefügt. Mehr braucht es - ausser der PDFium.dll - nicht, um PDF-Dokumente anzeigen und darin hin-und her scrollen zu können. Würde ich die Erzeugung des Dokumentes in die Form verlegen, müsste ich auch den ganzen Code des Basisframes in die Formunit verlegen - und das sind doch unter Berücksichtigung der Kommentare und meiner Report- und Memo-Ausgaben knappe tausend Zeilen. (Aktuell, mit allen meinen Änderungen: 1051)

Delbor 13. Dez 2017 09:12

AW: Seltsames Verhalten der Destruktoren
 
Hi Ghostwalker
Zitat:

Zitat von Ghostwalker (Beitrag 1388581)
Mal ganz platt gefrag:

Kann es sein, das einer der Aufrufe:

Delphi-Quellcode:
  FPages.Free;
  PDFPageClass.Free;
  FReportList.Free;
noch auf _Document zugreift ?

Nein. Keiner.
FPages ist ein TListobjekt des Basisframes, in dem die Handles der Seiten gespeichert werden.
PDFPageClass ist eine Instanz der TPDFPageClass , die von der im Basisframeprivat deklarierten Klasse PDFPage abgeleitet ist und ausser eine einzige Instanz zu Erzeugen nichts tut.
Im Verlaufe des Programmes deklarire ich eine Variable diese Typs (der eine meiner Änderungen ist), die nun in der Lage ist, eine Instanz des Vorfahren aufzuehmen. Ob ich da wirklich eine Instanz des Typs PDFPageClass brauche, bin ich mir im Moment jedoch nicht so klar.

Gruss
Delbor


Alle Zeitangaben in WEZ +1. Es ist jetzt 18:53 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