Delphi-PRAXiS
Seite 3 von 4     123 4      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Delphi Fehler beim Programm beenden (https://www.delphipraxis.net/186999-fehler-beim-programm-beenden.html)

Zacherl 23. Okt 2015 10:13

AW: Fehler beim Programm beenden
 
Du benutzt den VST (meiner Meinung nach) recht .. ungewöhnlich :D Ich schaue später daheim nochmal genauer drüber und poste ein paar Verbesserungsvorschläge.

Edit:
Also, ich bin immer sehr gut damit gefahren, wenn ich einen einzigen Record-Typ für alle Nodes verwendet habe. Das ist auch die einzig valide Anwendungsweise. In deinem Falle hast du nur "Glück", dass deine Records alle gleich groß sind.

Wenn ich verschiedene Node Typen habe, mache ich das immer so:
Delphi-Quellcode:
type
  TNodeType = (ntCustomer, ntBulding, ..);

  PNodeData = ^TNodeData;
  TNodeData = record
    NodeType: TNodeType;
    NodeObject: TObject;
  end;
Deine Methode über das NodeLevel zu differenzieren geht natürlich auch.

Die
Delphi-Quellcode:
NodeDataSize
ist aber in jedem Falle immer
Delphi-Quellcode:
SizeOf(TNodeData)
für alle Nodes.

Bezüglich der Objektverwaltung sehe ich zwei Möglichkeiten:
  1. Du behälst die einzelnen Objekte in einer gemeinsamen Liste (jeweils eine für Kunden, Gebäude, etc) und gibst dem NodeData Record jeweils nur einen Zeiger auf das Objekt mit. Die Freigabe der Objekte erfolgt dann gemeinsam beim Freigeben der Liste (Achtung: Je nachdem musst du manuell iterieren und
    Delphi-Quellcode:
    Free
    aufrufen.
    Diese Methode trennt ganz gut Daten von der Anzeige, allerdings hast du ein Problem, wenn zur Laufzeit dynamisch Einträge aus der Liste gelöscht oder hinzugefügt werden sollen, bzw. musst du dann doppelten Aufwand betreiben.
  2. Die zweite Möglichkeit wäre deshalb beim Hinzufügen der Nodes deine Objekte mit
    Delphi-Quellcode:
    Create
    zu konstruieren und den Zeiger wieder entsprechend zuzuweisen. Dann implementierst du noch das
    Delphi-Quellcode:
    OnFreeNode
    Event und rufst darin
    Delphi-Quellcode:
    NodeData^.NodeObject.Free
    auf.
    Beim Löschen einer Node wird dann automatisch auch das Objekt freigegeben.

Jens Hartmann 23. Okt 2015 19:27

AW: Fehler beim Programm beenden
 
EDIT: Videos doch gefunden...

Erstmal vielen Dank für die Tips. Ich werde das ganz jetzt mal durcharbeiten und versuchen zu verbessern. Ich werden das dann hier nochmal einstellen.

PS: Kann man irgendwo noch auf das "Stammtisch" Video zum VST zugreifen?

Gruß Jens

Jens Hartmann 23. Okt 2015 20:09

AW: Fehler beim Programm beenden
 
Groß gesagt gefunden und dann war es doch nur das vom Stammtisch 1...

Weiß jemand wo das Video vom Stammtisch 2 zu finden ist?

Jens Hartmann 24. Okt 2015 18:50

AW: Fehler beim Programm beenden
 
Hallo nochmal,

ich glaube das ich bei der Definition der Datenhaltung (Objekt, Record) schon was verbessern muss. Eventuell kann mit hier jemand einen Tip geben...

Aktuell habe ich 3 Klassen "Kunde", "Objekt" und "System"

Der Aufbau sieht wie folgt aus:

Delphi-Quellcode:
{ TObject für die Kundendaten }
type
  TOCustomers = class(TObject)
    private
      FID               : integer;        //ID
      FESID             : integer;        //ESID
      FName             : string;         //Name des Kunden
      FOrt              : string;         //Ort des Kunden
      FStraße           : string;         //Straße des Kunden
      FAnsprechpartnerId : integer;        //Ansprechpartnernummer
      FAnsprechpartner  : string;         //Ansprechpartner
      FAnsPosition      : string;         //Position/Stellung
      FEMail            : string;         //EMail des Ansprechpartners
      FTel1              : string;         //Telefonnummer 1 des Ansprechpartners
      FTel2              : string;         //Telefonnummer 2 des Ansprechpartners
      FFax              : string;         //Faxnummer des Ansprechpartners
      FBemerkung        : string;         //Kunden Zusatzinformation
    public
      property Kunden_Kundennummer : integer read FID write FID;
      property Kunden_ESKundennummer : integer read FESID write FESID;
      property Kunden_Kundenname : string read FName write FName;
      property Kunden_Ort : string read FOrt write FOrt;
      property Kunden_Straße : string read FStraße write FStraße;
      property Kunden_Bemerkung : string read FBemerkung write FBemerkung;
      property Ansprechpartner_Id : integer read FAnsprechpartnerId write FAnsprechpartnerId;
      property Ansprechpartner_Name : string read FAnsprechpartner write FAnsprechpartner;
      property Ansprechpartner_Position : string read FAnsPosition write FAnsPosition;
      property Ansprechpartner_EMail : string read FEMail write FEMail;
      property Ansprechpartner_Telefon1 : string read FTel1 write FTel1;
      property Ansprechpartner_Telefon2 : string read FTel2 write FTel2;
      property Ansprechpartner_Fax : string read FFax write FFax;
  end;

//Das Object für die Objektdaten und Systemdaten ist ähnlich. Teilweise andere Felder
Den Bezug zum VST stelle ich über das NodeLevel her.

Delphi-Quellcode:
  NodeLevel 0 = Datenbankpfad
  NodeLevel 1 = Kundendaten
  NodeLevel 2 = Objektdaten
  NodeLevel 3 = Systemdaten
  NodeLevel 4 = Menüpunkte zum System
Beim Click auf den jeweiligen Knoten (VST in Baumstruktur auf der Linken Programmseite), werden auf der rechten Seite in verschiedenen Frames die zugehörigen Datenangezeigt und zum editieren etc. zur Verfügung gestellt.

Da ich drei Objekte habe, stellt sich mir die Frage erneut nach dem
Delphi-Quellcode:
NodeDataSize
. Wie geh man sowas an.

Vorstellen könnte ich mir folgende alternativen.

1.

Das Object "TOCustomers" bekommt ein Feld
Delphi-Quellcode:
Objektdaten array of TOBuildings
und das Object TOBulidings bekommt ein Feld
Delphi-Quellcode:
Systemdaten array of TOSystems
2.

Die Objecte liegen ja je in einer eigenen Unit. Diese könnte man ja eventuell in einen Record einbinden:

Delphi-Quellcode:
type
  TRKundenDaten = record
    FKundedaten : TOCustomers;
    FObjektdaten : TOBuildings;
    FSystemdaten : TOSystems;
  end;

  PRKundenDaten = ^TRKundenDaten;
Vieleicht gibt es ja noch bessere/sinnvollere Wege. Die Variante 2 scheint mir aktuell am sinnvollsten. Dabei würde ich einen Record verwenden, welchen ich dann auch dem NodeDataSize zuweisen könnte.

Delphi-Quellcode:
vst.NodeDataSize:=sizeof(TRKundenDaten);
Weiterhin, wäre dann das OnFreeNode klar...

Delphi-Quellcode:
procedure TForm1.vstFreeNode(Sender: TBaseVirtualTree; Node: PVirtualNode);
var
  daten: PRMeineDaten;
begin
  daten := vst.GetNodeData(node);
  daten^.FKundendaten.free;
  daten^.FObjektdaten.free;
  daten^.FSystemdaten.free;
end;
Ich hoffe, das ich nicht ganz auf dem Holzweg bin...

TiGü 26. Okt 2015 08:38

AW: Fehler beim Programm beenden
 
Zitat:

Zitat von Jens Hartmann (Beitrag 1319675)
Eventuell kann mit hier jemand einen Tip geben...

Schaue dir den editierten Post von Zacherl an:
http://www.delphipraxis.net/1319525-post21.html

Wahrscheinlich hast du übersehen, das er ihn erweitert hat.
Da steht alles drin, was du wissen musst.
Punkt Nummer eins habe ich dir bspw. schon auf der vorigen Threadseite gezeigt.
Du musst die Vorschläge nur verstehen und umsetzen.

Jens Hartmann 26. Okt 2015 20:24

AW: Fehler beim Programm beenden
 
Zitat:

Zitat von TiGü
Wahrscheinlich hast du übersehen, das er ihn erweitert hat.
Da steht alles drin, was du wissen musst.
Punkt Nummer eins habe ich dir bspw. schon auf der vorigen Threadseite gezeigt.
Du musst die Vorschläge nur verstehen und umsetzen.

Hallo TiGü,

ja zu erstens, ich habe das übersehen. Ich glaube auch das da alles drin steht, aber der letze Punkt trifft leider auch zu. Ich habe das ganze jetzt versucht mal umzubauen. Als erstes hier mal mein neuer "record"

Delphi-Quellcode:
  type
  TRKundenDaten = record
    FKundedaten : TOCustomers;
    FObjektdaten : TOBuilding;
    FSystemdaten : TOSystems;
  end;

  PRKundenDaten = ^TRKundenDaten;
Aber der erste Crash kommt direkt beim einlesen...

Delphi-Quellcode:
var
  Node, CustomerNode, BuildingNode, SystemNode, ReportNode : PVirtualNode;
  Daten : PRKundenDaten;
begin
 try
    ...
    CustomerNode := vstKunden.AddChild(nil); //in diese Knoten wir nach nur der BD-Pfad geschrieben...

    //Kundendaten laden aus Kundendatenbank
        ...
        //Dann durch alle Datensätze mit einer Schleife...

        for i := 0 to DMMasterData.qryCustomerData.RecordCount -1 do
          begin
            try
              BuildingNode := vstKunden.AddChild(nil);
              Daten := vstKunden.GetNodeData(CustomerNode);
              //Mit Create und ohne versucht... Aber hier kracht es
              {Daten^.FKundedaten := TOCustomers.create;
              Daten^.FObjektdaten := TOBuilding.create;
              Daten^.FSystemdaten := TOSystems.create;}

              with Daten^ do
                begin
                  FKundedaten.Kunden_Kundennummer := DMMasterData.qryCustomerData.FieldByName('Kunden_Kundennummer').AsInteger;
                  ...
                end;

TiGü 27. Okt 2015 08:56

AW: Fehler beim Programm beenden
 
Ich würde den Weg von Zacherl gehen, also ein Record mit einen Enum-Typen zur Erkennung und einen allgemeinen TObject-Platzhalter, anstatt alle drei Objekte da rein zuverwursten.
Irgendwann kommt nämlich noch ein weiteres Objekt dazu und dann noch eins und noch eins...

Es wäre auch super mega klasse hilfreich, wenn du ein kleines Beispielprojekt zusammenstellen könntest.
Oft zeigt sich dann, dass das Problem an ganz anderen Stellen liegt.

Wenn man nämlich als hilfsbereiter Threadleser sich selber sowas ähnliches zusammenzimmert - siehe im folgenden - dann kracht es nicht!
So weiß man natürlich nicht, woran es genau in deinen Quelltext scheitert.

Delphi-Quellcode:
unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, VirtualTrees;

type
  TOCustomers = class(TObject)
  private
    FID: Integer; // ID
    FESID: Integer; // ESID
    FName: string; // Name des Kunden
    FOrt: string; // Ort des Kunden
    FStraße: string; // Straße des Kunden
    FAnsprechpartnerId: Integer; // Ansprechpartnernummer
    FAnsprechpartner: string; // Ansprechpartner
    FAnsPosition: string; // Position/Stellung
    FEMail: string; // EMail des Ansprechpartners
    FTel1: string; // Telefonnummer 1 des Ansprechpartners
    FTel2: string; // Telefonnummer 2 des Ansprechpartners
    FFax: string; // Faxnummer des Ansprechpartners
    FBemerkung: string; // Kunden Zusatzinformation
  public
    property Kunden_Kundennummer: Integer read FID write FID;
    property Kunden_ESKundennummer: Integer read FESID write FESID;
    property Kunden_Kundenname: string read FName write FName;
    property Kunden_Ort: string read FOrt write FOrt;
    property Kunden_Straße: string read FStraße write FStraße;
    property Kunden_Bemerkung: string read FBemerkung write FBemerkung;
    property Ansprechpartner_Id: Integer read FAnsprechpartnerId write FAnsprechpartnerId;
    property Ansprechpartner_Name: string read FAnsprechpartner write FAnsprechpartner;
    property Ansprechpartner_Position: string read FAnsPosition write FAnsPosition;
    property Ansprechpartner_EMail: string read FEMail write FEMail;
    property Ansprechpartner_Telefon1: string read FTel1 write FTel1;
    property Ansprechpartner_Telefon2: string read FTel2 write FTel2;
    property Ansprechpartner_Fax: string read FFax write FFax;

  end;

  TOBuilding = class
  end;

  TOSystems = class

  end;

  TRKundenDaten = record
    FKundedaten: TOCustomers;
    FObjektdaten: TOBuilding;
    FSystemdaten: TOSystems;
  end;

  PRKundenDaten = ^TRKundenDaten;

type
  TForm1 = class(TForm)
    vstKunden: TVirtualStringTree;
    procedure FormCreate(Sender: TObject);
    procedure VstKundenFreeNode(Sender: TBaseVirtualTree; Node: PVirtualNode);
  private
  public
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
var
  I : Integer;
  CustomerNode, BuildingNode, SystemNode: PVirtualNode;
  Daten: PRKundenDaten;
begin
  vstKunden.BeginUpdate;
  try
    vstKunden.Clear;

    CustomerNode := vstKunden.AddChild(nil);
    vstKunden.InvalidateNode(CustomerNode);
    vstKunden.NodeDataSize := SizeOf(TRKundenDaten);

    for I := 0 to 10 do
    begin
      BuildingNode := vstKunden.AddChild(nil);
      Daten := vstKunden.GetNodeData(CustomerNode);
      // das folgende geht anstandslos
      Daten^.FKundedaten := TOCustomers.Create;
      Daten^.FObjektdaten := TOBuilding.Create;
      Daten^.FSystemdaten := TOSystems.Create;
    end;

    vstKunden.SortTree(0, sdAscending, True);
  finally
    vstKunden.EndUpdate;
  end;
end;

procedure TForm1.VstKundenFreeNode(Sender: TBaseVirtualTree; Node: PVirtualNode);
var
  Kunden_Daten: PRKundenDaten;
begin
  case vstKunden.GetNodeLevel(Node) of
    0:
    begin
      Kunden_Daten := Sender.GetNodeData(Node);
      Finalize(Kunden_Daten^);
    end;
  end;
end;

end.

Jens Hartmann 27. Okt 2015 19:49

AW: Fehler beim Programm beenden
 
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo TiGü,

als Anlage mal das Musterprojekt genau nach Deinen vorgaben. Trotzallem immer noch der gleiche Fehler. Ich habe auch mal mit FastMM eingebunden. Das EventLog liegt dem Musterprojekt bei. Selbst in diesem kleinen Projekt entsteht der gleiche Fehler. Ich habe auch nochmal mein aktuelles Projekt geprüft und bearbeitet. Ich habe diese genau nach Deinem Vorschlag zusammengebaut und wie im Musterprojekt kommt die Fehlermeldung.

Grundlegend habe ich noch Fragen zu Zacherls vorschlagen und der Umsetzung. Das aber vieleicht später. Erstmal stellt sich mir die Frage, ob die Komponenten eventuell verbogen oder falsch konfiguriert ist. 2 Alternative, kann es mit Windows 10 zusammenhängen?

Gruß Jens

bcvs 28. Okt 2015 07:50

AW: Fehler beim Programm beenden
 
Zwei Sachen sind mir aufgefallen:

1.
Delphi-Quellcode:
vstKunden.NodeDataSize := SizeOf(TRKundenDaten);
muss vor dem ersten AddChild stehen, sonst weiß der VST ja nicht, wie groß der Node sein soll und nachfolgende Speicheroperationen laufen ins (n)irgendwo.

2.
Wie schon gesagt, solltest du die Objekte in einer separaten Liste speichern und diese selbst wieder freigeben. Dann ist die Zuständigkeit sauber geregelt und du kannst auf das OnFreeNode komplett verzichten. Der VST arbeitet nur mit den Daten, der braucht sich nicht darum zu kümmern wo die herkommen und ob die freigegeben werden müssen.

Delphi-Quellcode:
procedure TForm1.FormCreate(Sender: TObject);
var
  I : Integer;
  CustomerNode, BuildingNode, SystemNode: PVirtualNode;
  Daten: PRKundenDaten;
  fk:TOCustomers;
  fo:TOBuilding;
  fs:TOSystems;
begin
  vstKunden.NodeDataSize := SizeOf(TRKundenDaten);

  KundenList:=TObjectList.create; // separate Liste
  vstKunden.BeginUpdate;
  try
    vstKunden.Clear;

    CustomerNode := vstKunden.AddChild(nil);
    vstKunden.InvalidateNode(CustomerNode);

    for I := 0 to 10 do
    begin
      BuildingNode := vstKunden.AddChild(nil);
      Daten := vstKunden.GetNodeData(CustomerNode);
      // das folgende geht anstandslos
      fk := TOCustomers.Create;
      fo := TOBuilding.Create;
      fs := TOSystems.Create;

      Daten^.FKundedaten := fk;
      Daten^.FObjektdaten := fo;
      Daten^.FSystemdaten := fs;
      KundenList.Add(fk); // wahrscheinlich reicht hier aber auch ein Object pro Node
      KundenList.Add(fo);
      KundenList.Add(fs);
    end;

    vstKunden.SortTree(0, sdAscending, True);
  finally
    vstKunden.EndUpdate;
  end;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  KundenList.free;
end;

TiGü 28. Okt 2015 09:03

AW: Fehler beim Programm beenden
 
Zitat:

Zitat von Jens Hartmann (Beitrag 1319874)
als Anlage mal das Musterprojekt genau nach Deinen vorgaben. Trotzallem immer noch der gleiche Fehler.

Ändere mal in der for-Schleife diese Zeilen:
Delphi-Quellcode:
   
 for I := 0 to 10 do
    begin
      BuildingNode := vstKunden.AddChild(nil);
      Daten := vstKunden.GetNodeData(CustomerNode);
...
in folgende Zeilen:

Delphi-Quellcode:
   
 for I := 0 to 10 do
    begin
      BuildingNode := vstKunden.AddChild(nil);
      Daten := vstKunden.GetNodeData(BuildingNode);
...
Außerdem bitte noch das OnFreeNode ergänzen:
Delphi-Quellcode:
procedure TForm1.vstKundenFreeNode(Sender: TBaseVirtualTree; Node: PVirtualNode);
var
  Kunden_Daten: PRKundenDaten;
begin
  case vstKunden.GetNodeLevel(Node) of
    0:
    begin
      Kunden_Daten := Sender.GetNodeData(Node);
      Kunden_Daten^.FKundedaten.Free;
      Kunden_Daten^.FObjektdaten.Free;
      Kunden_Daten^.FSystemdaten.Free;
      Finalize(Kunden_Daten^);
    end;
  end;
end;
Und jetzt darfst du gerne mit den Kopf auf den Tisch schlagen. :wall: :mrgreen:

PS: Trotzdem wäre der Weg mit externer Datenhaltung über eine Objektliste langfristig besser!
Bitte beschäftige dich damit.


Alle Zeitangaben in WEZ +1. Es ist jetzt 09:31 Uhr.
Seite 3 von 4     123 4      

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