Übergabe der Datenbankverbindung aus einem anderen Formular (Access Violation)

Für Themen zu Datenbanken und Zugriff auf diese. Auch für Datenbankkomponenten.
Antworten
dave_christoph
Beiträge: 4
Registriert: Di 7. Mai 2024, 15:11

Übergabe der Datenbankverbindung aus einem anderen Formular (Access Violation)

Beitrag von dave_christoph »

Hallo zusammen,

ich beschäftige mich neu mit Lazarus (Version 3.2) und bin in Verbindung mit Datenbanken (ich nutze SQL-Lite) auf ein Problem gestoßen.
Aus meinem Hauptmenü (Hauptformular) möchte ich die Datenbankverbindung an ein weiteres Formular übergeben, damit dort über ein Formular ein Datensatz hinzugefügt werden kann.

Alles wird richtig ausgeführt und kompiliert.
Allerdings stoße ich leider immer wieder auf den Fehler "Access Violation", der die Anwendung komplett crasht, sobald ein INSERT auf die Datenbank ausgeführt werden soll.
Genau bei der Ausführung dieses Codes:

Code: Alles auswählen

procedure TFormDatenEinlesen.btnOKClick(Sender: TObject);
var
  DataSetID: Integer;
  Value: String;
begin
  try
    ExternalSQLQuery.SQL.Text := 'INSERT INTO DataSet (CreationDate, Name, Comment) VALUES (:CreationDate, :Name, :Comment)';
    ExternalSQLQuery.ParamByName('CreationDate').AsString := FormatDateTime('yyyy-mm-dd', Now);
    ExternalSQLQuery.ParamByName('Name').AsString := EditName.Text;
    ExternalSQLQuery.ParamByName('Comment').AsString := MemoComment.Text;
    ExternalSQLQuery.ExecSQL;

    ExternalSQLQuery.SQL.Text := 'SELECT last_insert_rowid() AS LastID';
    ExternalSQLQuery.Open;
    DataSetID := ExternalSQLQuery.Fields[0].AsInteger;
    ExternalSQLQuery.Close;

    for Value in MemoDaten.Lines do
    begin
      ExternalSQLQuery.SQL.Text := 'INSERT INTO UnsortedValues (DataSetID, Value) VALUES (:DataSetID, :Value)';
      ExternalSQLQuery.ParamByName('DataSetID').AsInteger := DataSetID;
      ExternalSQLQuery.ParamByName('Value').AsString := Value;
      ExternalSQLQuery.ExecSQL;
    end;

    ModalResult := mrOk;
  except
    on E: Exception do
    begin
      ShowMessage('Fehler bei der Datenverarbeitung: ' + E.Message);
    end;
  end;
end;           

Ich habe die entsprechenden *.pas Dateien angehängt.
Vielleicht hat jemand eine Idee oder findet den Fehler?

ich glaube es liegt an den Parametern (:CreationDate, :Name, :Comment)... Aber keine Ahnung was ich ändern soll.

Ich danke Euch und bin für jede Hilfe dankbar!

Viele Grüße
Dave
Zuletzt geändert von dave_christoph am Mi 8. Mai 2024, 07:59, insgesamt 1-mal geändert.

charlytango
Beiträge: 1087
Registriert: Sa 12. Sep 2015, 12:10
OS, Lazarus, FPC: Laz stable (2.2.6, 3.x)
CPU-Target: Win 32/64, Linux64
Wohnort: Wien

Re: Übergabe der Datenbankverbindung aus einem anderen Formular (Access Violation)

Beitrag von charlytango »

Nachdem du nur die *.PAS Dateien zeigst und die dazu gehörenden *.LFM Dateien fehlen ist es schwierig etwas valides zu sagen.
In Fällen wie diesem ist es geraten eine Demo-Applikation hochzuladen die auch funktionieren könnte.

Genau für solche Fälle hat Lazarus eine Export-Routine (Menü: Project - Publish Project) die ein Projekt samt allen nötigen Dateien (die über die Lazarus Kontrolle hat) exportiert. ggfs bleiben INI-Dateien oder Datenbankdateien über und müssen in dem Export-Dialog per Dateiendung hinzugefügt werden. Dann hast du schonmal ein Projekt das beim Export auch kompilierbar ist.

Sonst gehen nur die üblichen Ratschläge:
dave_christoph hat geschrieben: Di 7. Mai 2024, 15:20 Allerdings stoße ich leider immer wieder auf den Fehler "Access Violation"
Das lässt mich vermuten dass du ein Objekt angreifst das zu diesem Zeitpunkt nicht oder noch nicht existiert.
Das trifft z.B in TFormDatenEinlesen.btnOKClick Zeile 113 zu in dem "Value" nicht initialisiert ist.

An welcher Stelle im Code tritt der Fehler denn konket auf? Schon mal mit dem Debugger durchgesteppt? Ich vermute, Zeile 117 wäre ein Kandidat

dave_christoph
Beiträge: 4
Registriert: Di 7. Mai 2024, 15:11

Re: Übergabe der Datenbankverbindung aus einem anderen Formular (Access Violation)

Beitrag von dave_christoph »

Hallo charlytango,

vielen Dank für Deine Antwort.
Ich habe das komplette Projekt als Anhang (siehe Projekt1.zip) hochgeladen.

Der Fehler taucht in Zeile 104 im Formular "formdateneinlesen.pas" auf:
ExternalSQLQuery.SQL.Text := 'INSERT INTO DataSet (CreationDate, Name, Comment) VALUES (:CreationDate, :Name, :Comment)';

Eigentlich sollte doch "Value" richtig initialisiert sein, oder?

Zudem habe ich das Projekt nochmal etwas angepasst (siehe Projekt2.zip) und die Tabellenstruktur und die Prozeduren etwas angepasst.
Allerdings erhalte ich jetzt schon beim Aufruf des Formulars eine Violation-Meldung!
Was mache ich da falsch? Ist meine Formular-Datei oder irgendwas an den Projektdateien vielleicht bereits irgendwie korrupt?

Ich wäre sehr dankbar, wenn jemand sowohl die Projekt1.zip als auch Projekt2.zip mal anschauen und mir weiterhelfen könnte.
Irgendwo mache ich einen grundlegenden Fehler.

Vielen, vielen Dank!

Viele Grüße
Dave

Update: Ich habe es jetzt geschafft über ein Interface (IDataExchange) die Access Violation Errors zu eliminieren (siehe Projekt3.zp).
Allerdings wird in meinem Grid im ersten Formular nur MEMO als Datensatz Inhalt angezeigt. Woran kann das liegen bzw. was muss ich ändern?
Zuletzt geändert von dave_christoph am Do 9. Mai 2024, 08:21, insgesamt 1-mal geändert.

charlytango
Beiträge: 1087
Registriert: Sa 12. Sep 2015, 12:10
OS, Lazarus, FPC: Laz stable (2.2.6, 3.x)
CPU-Target: Win 32/64, Linux64
Wohnort: Wien

Re: Übergabe der Datenbankverbindung aus einem anderen Formular (Access Violation)

Beitrag von charlytango »

ich hab dein Projekt project1 mal kurz überflogen und sehe da einiges was mir komisch vorkommt und ich nicht so machen würde.

Deine DB-Verbindung im Hauptformular einzubauen, beschränkt dich einfach in der Verwendbarkeit.
Um (auch GUI-Komponenten) zu verwalten gibt es seit Delphi 2 das Datenmodul(Data Module) in das ich einmal grundsätzlich alle Datenbankkomponenten hineinpacken würde, die mit der DB-Verbindung zu tun haben. Also Connection, Transaktion und alle Nebenfunktionen. Ein Datenmodul ist quasi ein "Formular" das nicht angezeigt wird.

Sorge dafür dass dieses Datenmodul einen klingenden Namen bekommt (der wird dann unter var in der Unit eingetragen).
Das Datenmodul kannst du dann in allen Formularen mit DB-Anbindung in der USES Klausel eintragen und verwenden. Somit kann man aus allen Formularen auf die DB-Verbindung zugreifen und muss sie nicht immer irgendwie übergeben.
Wichtig ist, sicher zu stellen dass das Datenmodul (meinetwegen auch automatisch) erstellt wird bevor man darauf zugreift, sonst gibts eine Access Violation.

Alle datensensitiven Komponenten verwalte ich im Formular (Query, Datasource) und weise beim OnCreate die Connection aus dem Datenmodul zu.

Eine ziemlich komplette Umsetzung davon mit iniFile für DB-Einstellungen und einigen Gimmicks wie DB-Wechselkannst du dir hier runterladen und verwenden.

wp_xyz
Beiträge: 5191
Registriert: Fr 8. Apr 2011, 09:01

Re: Übergabe der Datenbankverbindung aus einem anderen Formular (Access Violation)

Beitrag von wp_xyz »

Habe Projekt1 heruntergeladen, übersetzt und hoffentlich richtig bedient.

Ja, da gibt es eine Zugriffsverletzung, wenn man im zweiten Formular auf den "Bestätigen"-Button klickt (btnOK). Es ist die Zeile "ExternalSQLQuery.SQL.Text := 'INSERT INTO DataSet ...". Setze ich da einen Breakpoint und fahre ich mit der Maus über das Wort "ExternalSQLQuery", sehe ich wie erwartet im Popup-Fenster, dass diese Variable nil ist - das muss krachen. Es gibt eine Initialisierung dafür, im Constructor Create, der aber eine etwas seltsame Aufrufliste mit weiteren Parametern hat. Da steht zwar ein "reintroduce" dahinter, aber reicht das, dass das Formular mit dem richtigen Constructor erzeugt wird? Zumal das Formular in der Liste der automatisch zu erzeugenden Formulare steht... Und tatsächlich, ein Blick in den Quellcode von TApplication zeigt, dass Application.CreateForm (das im Projekt-Formular ein automatisch zu erzeugendes Formular erzeugt) nur den Standard-Constructor mit dem einen Parameter AOwner aufruft, so dass dein eigener Constructor und damit die Initialisierung von ExternalSQLQuery nicht ausgeführt wird.

Ich habe daraufhin in den Projekt-Optionen auch das Formular frmDatenEinlesen in die Liste der verfügbaren Formulare verschoben (wo das mit dem standardnamen schon stand), und damit war der Crash weg.

------------

Beim zweiten Projekt bekomme ich beim Klick auf dem Button im 1. Formular, der das zweite öffnen soll, eine Fehlermeldung, dass etwas nicht mit dem OnClick-Handler nicht stimmt. War etwas schwer zu finden, weil die meldung gar nicht den geklickten Button betroffen hat, sondern den im neu erzeugten Formular. Schließlich habe ich gesehen, dass du die Zeile "procedure BtnSelectFileClick(Sender: TObject)" in den "public"-Abschnitt der Formulardeklaration von TFormDataEinlesen geschrieben hast; im Objektinspektor steht "BtnSelectFileClick" aber in der OnClick-Zeile des Buttons. Das ist ein Problem für die IDE, denn alles was im Objektinspektor steht, muss eine "published" property sein, nicht "public"! In der Formulardeklaration ist alles "published", was direkt unter der Typ-deklaration steht, bis zum ersten "private", "protected" oder "public". Das heißt, du musst die procedure-Zeile nach oben, überhalb "private" verschieben. Damit gibt es keinen Absturz mehr. (Alternativ könntest du auch "public" lassen, aber dann müsstest du die Zeile im Objekt-Inspektor löschen und das Event manuell dem Button zuweisen, z.b. in TFormDatenEinlesen.Create: BtnSelectFile.OnClick := @BtnSelectFileClick). Ich fasse hier mal die Änderung zusammen:

Code: Alles auswählen

  TFormDatenEinlesen = class(TForm)
    BtnOK: TButton;
    BtnCancel: TButton;
    BtnSelectFile: TButton;
    EditName: TEdit;
    EditComment: TEdit;
    OpenDialog: TOpenDialog;  // Dialog für Dateiauswahl
    procedure BtnSelectFileClick(Sender: TObject);                // <---- VON "public" HIERHER KOPIERT
  private
    SelectedFilePath: string;  // Pfad zur zuletzt ausgewählten Datei
    FSQLite3Connection: TSQLite3Connection;
    FSQLTransaction: TSQLTransaction;
    FQuery: TSQLQuery; // Hinzufügen einer TSQLQuery-Komponente
  public
    property SQLite3Connection: TSQLite3Connection read FSQLite3Connection write FSQLite3Connection;
    property SQLTransaction: TSQLTransaction read FSQLTransaction write FSQLTransaction;
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
//    procedure BtnSelectFileClick(Sender: TObject);     // <--- DAS MUSS "published" SEIN.
    procedure BtnOKClick(Sender: TObject);
    procedure BtnCancelClick(Sender: TObject);
  end; 
Eine Bemerkung hier noch zu dem nun normalen Constructor:

Code: Alles auswählen

constructor TFormDatenEinlesen.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);

  FQuery := TSQLQuery.Create(nil); // Initialisiere die TSQLQuery-Komponente
  FQuery.DataBase := FSQLite3Connection;
  FQuery.Transaction := FSQLTransaction;
end; 
Die Zuweisungen von FSQLite3Connection an FQuery.Database und FSQLTransaction an FQuery.Transaction sind hier nutzlos, da die Connection und Transaction, die du ja als Properties realisierst, noch gar nicht gesetzt sein können. Und du machst da richtigerweise auch im aufrufenden Code.
Zuletzt geändert von wp_xyz am Mi 8. Mai 2024, 16:20, insgesamt 3-mal geändert.

charlytango
Beiträge: 1087
Registriert: Sa 12. Sep 2015, 12:10
OS, Lazarus, FPC: Laz stable (2.2.6, 3.x)
CPU-Target: Win 32/64, Linux64
Wohnort: Wien

Re: Übergabe der Datenbankverbindung aus einem anderen Formular (Access Violation)

Beitrag von charlytango »

wenn du mit meinem DB-Zugriff arbeiten magst hab ich dir ein Beispiel mit dienen Formularen gebaut
Dateianhänge
example2.zip
(222.94 KiB) 68-mal heruntergeladen

dave_christoph
Beiträge: 4
Registriert: Di 7. Mai 2024, 15:11

Re: Übergabe der Datenbankverbindung aus einem anderen Formular (Access Violation)

Beitrag von dave_christoph »

Hallo zusammen,

vielen Dank für Eure Antworten.
Das hilft mir sehr für das Verständnis!

Ich werde mich nochmal dran setzen und mein Projekt entsprechend überarbeiten.

Viele Grüße
Dave

charlytango
Beiträge: 1087
Registriert: Sa 12. Sep 2015, 12:10
OS, Lazarus, FPC: Laz stable (2.2.6, 3.x)
CPU-Target: Win 32/64, Linux64
Wohnort: Wien

Re: Übergabe der Datenbankverbindung aus einem anderen Formular (Access Violation)

Beitrag von charlytango »

Falls du mit meiner DB-Verbindung arbeitest, lass mich bitte wissen wie du damit zurecht kommst oder wo Probleme auftreten, damit ich die Komponente ggfs verbessern kann
Danke

dave_christoph
Beiträge: 4
Registriert: Di 7. Mai 2024, 15:11

Re: Übergabe der Datenbankverbindung aus einem anderen Formular (Access Violation)

Beitrag von dave_christoph »

@charlytango
Das mache ich gerne! Deine DB-Lösung sieht aber stabil und übersichtlich aus! :)

@all
Ich habe mich auch nochmal an meine Lösung mit einem interface (idataexchange) gesetzt.
Es funktioniert auch alles, allerdings habe ich ein seltsames Verhalten an einer Stelle im Code:

In Zeile 274 im Modul "MAINMENU":

Code: Alles auswählen

frmDatenEinlesen.DataExchange := Self;
In der Ausführung in der IDE schmeißt mich das Programm mitten in der Code-Ausführung an o.g. Stelle raus und unterbricht das Programm (ohne Debug Rückmeldung).
Kompiliere ich das Programm jedoch und öffne es als EXE, funktioniert alles wie es soll.

Woran kann das liegen? Habt ihr eine Lösung dafür?
Ich habe nochmals das vollständige Projekt (siehe Anhang) beigefügt.

Vielen Dank!

Viele Grüße
Dave
Dateianhänge
Projekt4.zip
(1.56 MiB) 49-mal heruntergeladen

Antworten