AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

Record-Variable in Klasseninstanz setzen

Ein Thema von Perlsau · begonnen am 9. Dez 2012 · letzter Beitrag vom 10. Dez 2012
Antwort Antwort
Seite 1 von 2  1 2      
Perlsau
(Gast)

n/a Beiträge
 
#1

Record-Variable in Klasseninstanz setzen

  Alt 9. Dez 2012, 11:39
Moin allerseits,

ich bin offensichtlich noch nicht so der Held in der Erstellung und Verwendung von Klassen, deshalb hab ich damit auch noch ein paar Verständnis-Probleme ... ich arbeite übrigens bis Ende des Jahres an einem Win7-Kundenrechner mit RadStudio XE2 Architect und nicht wie gewohnt im RadStudio 2009 pro ...

In einer Klasse, die hauptsächlich auf die Mainform zugreift und dort Dinge wie Statusbar und diverse Label-Captions verändern soll, habe ich einen Record definiert:

Delphi-Quellcode:
unit Main_Zugriff;

INTERFACE

USES
  Vcl.Forms, System.Classes, System.SysUtils, JvRichEdit, JvDBRichEdit;

TYPE
  TMain_Zugriff = CLASS

  PRIVATE

  PUBLIC
    TYPE
      TTab_User = RECORD
                    Titel_Index : Integer;
                    Titel_SortIndex : Integer;
                    Titel_SortOrder : Boolean;
                    Titel_SortText : String;
                  END;

    VAR
      User_Tab : TTab_User;

    CONSTRUCTOR Create();
    DESTRUCTOR Destroy; override;

    PROCEDURE Set_FormMain(Const Links, Oben, Breit, Hoch : Integer; Const Maximiert : Boolean);
    PROCEDURE Get_FormMain(Var Links, Oben, Breit, Hoch : Integer; Var Maximiert : Boolean);
    PROCEDURE Set_StatusBar(Const Spalte : Integer; Const Wert : String);

  END;

IMPLEMENTATION

USES
    UnitMain;
Wenn ich nun in einer AfterScroll-Ereignisbehandlung, die sich in der Unit zur Form DatMod (Datamodule) befindet, die Variable User_Tab.Titel_Index setzen möchte, erhalte ich eine Zugriffsverletzung:

Im Projekt MovieBase3.exe ist eine Exception der Klasse $C0000005 mit der Meldung 'c0000005 ACCESS_VIOLATION' aufgetreten.

Delphi-Quellcode:
...
VAR
  DatMod: TDatMod;

IMPLEMENTATION
{%CLASSGROUP 'System.Classes.TPersistent'}
{$R *.dfm}

USES
    Main_Zugriff;

VAR
   MainZu : TMain_Zugriff;

procedure TDatMod.DataModuleCreate(Sender: TObject);
begin
     MainZu := TMain_Zugriff.Create;
end;

procedure TDatMod.DataModuleDestroy(Sender: TObject);
begin
     FreeAndNil(MainZu);
end;

procedure TDatMod.Aset_TitelAfterScroll(DataSet: TDataSet);
VAR
   Idx_Titel : Integer;
begin
     Idx_Titel := Aset_Titel.FieldByName('Idx_Titel').AsInteger;
     MainZu.Set_StatusBar(0,'Idx: '    + IntToStr(Idx_Titel));
     MainZu.Set_StatusBar(1,'Anzahl: ' + IntToStr(Aset_Titel.RecordCount));
     MainZu.Set_StatusBar(2,'RecNo: '  + IntToStr(Aset_Titel.RecNo));
     MainZu.User_Tab.Titel_Index := Idx_Titel; // hier wird die Zugriffsverletzung ausgelöst
end;
Ein weitgehend ähnliches Konstrukt im selben Projekt funktioniert aber einwandfrei:
Delphi-Quellcode:
UNIT MainTools;

INTERFACE

USES
    System.SysUtils, System.Classes, Vcl.Dialogs, Data.DB, Data.Win.ADODB,
    Datasnap.DBClient, Data.DBXFirebird, Data.SqlExpr, Data.FMTBcd, Datasnap.Provider;

TYPE
  TMainTools = CLASS

  PRIVATE
    CONST
       K = ';';
       Km = ',';
       cPROV_Name = 'SQLOLEDB.1';
       cDB_Name = 'FILME';
       cServer_Name = 'SPEEDY';
       cInt_Secur = 'SSPI';
       cUser_Name = 'MsMasterAdmin';
       cApp_Name = 'MovieBase3';

    VAR
       PROV_Name,
       DB_Name,
       Server_Name,
       Int_Secur,
       User_Name,
       App_Name,
       Verbindung : String;

  PUBLIC

    TYPE
      TRec_User = RECORD
                   Pfad_Main,
                   Name,
                   Pass : String;
                  END;

    VAR
      Rec_User : TRec_User;
      SuchText : String;


    CONSTRUCTOR Create();
    DESTRUCTOR Destroy; override;

    FUNCTION Verbinden_Datenbank : Boolean;
    PROCEDURE Trennen_Datenbank;
    FUNCTION Verbinden_Datasets : Boolean;
    PROCEDURE Trennen_Datasets;
    PROCEDURE Einstellungen_Setzen;
    PROCEDURE Einstellungen_Speichern;

  END;

IMPLEMENTATION

USES
  UnitData, Main_Zugriff;

VAR
   MZGF : TMain_Zugriff;
Wenn ich hier vom Hauptformular aus z.B. die Variable Rec_User.Pfad_Main setze, gibt's keine Zugriffsverletzung:

Delphi-Quellcode:
...
var
  FormMain: TFormMain;

IMPLEMENTATION
{$R *.dfm}


USES
    MainTools, UnitData;

VAR
   MNTL : TMainTools;

procedure TFormMain.FormCreate(Sender: TObject);
begin
     MNTL := TMainTools.Create;
     MNTL.Rec_User.Pfad_Main := ExtractFilePath(ParamStr(0)); // keine Access Violation
     MNTL.SuchText := ''; // keine Access Violation
end;
...
  Mit Zitat antworten Zitat
Benutzerbild von DeddyH
DeddyH

Registriert seit: 17. Sep 2006
Ort: Barchfeld
27.541 Beiträge
 
Delphi 11 Alexandria
 
#2

AW: Record-Variable in Klasseninstanz setzen

  Alt 9. Dez 2012, 11:54
Funktioniert es denn, wenn Du von Nested Types auf "normale" umsteigst? Wobei mir übrigens der Sinn so einer Klasse nicht ganz klar ist.
Detlef
"Ich habe Angst vor dem Tag, an dem die Technologie unsere menschlichen Interaktionen übertrumpft. Die Welt wird eine Generation von Idioten bekommen." (Albert Einstein)
Dieser Tag ist längst gekommen
  Mit Zitat antworten Zitat
Perlsau
(Gast)

n/a Beiträge
 
#3

AW: Record-Variable in Klasseninstanz setzen

  Alt 9. Dez 2012, 12:42
Funktioniert es denn, wenn Du von Nested Types auf "normale" umsteigst? Wobei mir übrigens der Sinn so einer Klasse nicht ganz klar ist.
Keine Ahnung, was Nested Types bedeutet ... aber ich hab gleich mal nachgeschlagen und festgestellt, daß man darunter die Erstellung einer Klasse in einer Klasse (Nest) versteht. Also müßte ich den Record nicht innerhalb des anderen Types deklarieren:

Delphi-Quellcode:
unit Main_Zugriff;

INTERFACE

USES
  Vcl.Forms, System.Classes, System.SysUtils, JvRichEdit, JvDBRichEdit;

TYPE
    TTab_User = RECORD
                    Titel_Index : Integer;
                    Titel_SortIndex : Integer;
                    Titel_SortOrder : Boolean;
                    Titel_SortText : String;
                END;

  TMain_Zugriff = CLASS

  PRIVATE

  PUBLIC
      User_Tab : TTab_User;
      Titel_Index : Integer;
Das ändert jedoch nichts, es erscheint derselbe Fehler. Nun habe ich der Klasse TMain_Zugriff eine weitere Procedure beigefügt, die User_Tab.Titel_Index ändern soll:
Delphi-Quellcode:
procedure TMain_Zugriff.Set_Titel_Index(Idx: Integer);
begin
     User_Tab.Titel_Index := Idx;
     Set_StatusBar(4,IntToStr(User_Tab.Titel_Index));
end;
Rufe ich diese Procedure von Create aus auf, dann funktioniert sie:
Delphi-Quellcode:
constructor TMain_Zugriff.Create;
begin
     Set_Titel_Index(11111);
end;
Rufe ich sie jedoch von Aset_TitelAfterScroll (DatMod) aus auf, verweigert sie den Dienst:
Delphi-Quellcode:
procedure TDatMod.Aset_TitelAfterScroll(DataSet: TDataSet);
VAR
   Idx_Titel : Integer;
begin
     ...
     MainZu.Set_Titel_Index(Idx_Titel);
end;
Die anderen Proceduren wie z.B. Set_StatusBar funktionieren aber von Aset_TitelAfterScroll aus ... aber die ändern auch keine Variablen der eigenen Klasse, sondern die einer anderen Klasse (TFormMain).

Der Sinn dieser Klasse besteht darin (oder soll darin bestehen), nicht vom Datenmodul aus die Mainform referenzieren zu müssen, wenn Ereignisse im Datenmodul Veränderungen in der Mainform erfordern. Doch warum eigentlich nicht, die Mainform referenziert ja das Datenmodul nicht, das läuft alles über die Unit MainTools. Deshalb hab ich das jetzt so gemacht:

In der Unit UnitData (Form DatMod) hab ich als ersten Type meine Record-Deklaration platziert. Im Public-Teil von DatMod steht jetzt die Variablen-Deklaration des Records. So gibt's keine Zugriffsverletzung mehr ... Ob das die endgültige Lösung ist, wird sich zeigen.

Aber ich glaube, daß ich sowieso irgendwie auf dem Holzweg bin mit meiner Vorstellung über die Trennung von GUI und Methoden ... Vielleicht sollte ich mal SirRufo fragen, wie eine Kapselung für Datenbank-Komponenten aussehen muß ... immerhin propagiert er die ja des öfteren mal ...
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
11.009 Beiträge
 
Delphi 12 Athens
 
#4

AW: Record-Variable in Klasseninstanz setzen

  Alt 9. Dez 2012, 13:10
Ich vermute mal, daß das AfterScroll-Event vor dem DataModuleCreate ausgelöst wird (passiert, wenn das DataSet zur DesignTime auf Active gesetzt ist). Damit wäre die Instanz von MainZu noch nicht erstellt und es kommt zu der Schutzverletzung.
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
Perlsau
(Gast)

n/a Beiträge
 
#5

AW: Record-Variable in Klasseninstanz setzen

  Alt 9. Dez 2012, 13:27
Ich vermute mal, daß das AfterScroll-Event vor dem DataModuleCreate ausgelöst wird (passiert, wenn das DataSet zur DesignTime auf Active gesetzt ist). Damit wäre die Instanz von MainZu noch nicht erstellt und es kommt zu der Schutzverletzung.
Das hatte ich zu allererst überprüft: Die Datenbankverbindung wird erst nach dem Start in OnShow hergestellt. Ich achte immer darauf, daß die Verbindungskomponente (hier ADOConnection zur Verbindung mit einem MsSQL-Server) nicht zufällig auf connected := true steht, weil ich vielleicht zuvor im Feld-Editor eines Datasets war und dort was manipuliert habe. Das ist bei mir schon Routine, vor dem Betätigen der F9-Taste, wenn ich zuvor im Datenmodul zugange war, die Connect-Komponente zu überprüfen. Beim Setzen auf false werden ja auch alle offenen Datasets inaktiv ... Außerdem würden, wenn deine Vermutung zuträfe, auch die anderen Proceduren wie z.B. Set_StatusBar, die ja funktionieren, eine Zugriffsverletzung auslösen.

So wie ich's nun gelöst habe, ist es sowieso viel übersichtlicher: Die Variablen, die andere Units bzw. Klassen über den Zustand der Datenbank informieren sollen, lege ich im Datenmodul ab. Von dort holt sich die Klasse TMainTools alle notwendigen Informationen.
  Mit Zitat antworten Zitat
Benutzerbild von Bummi
Bummi

Registriert seit: 15. Jun 2010
Ort: Augsburg Bayern Süddeutschland
3.470 Beiträge
 
Delphi XE3 Enterprise
 
#6

AW: Record-Variable in Klasseninstanz setzen

  Alt 9. Dez 2012, 16:40
Wird denn die Variable von TMain_Zugriff
VAR
User_Tab : TTab_User;

initialisiert?
Thomas Wassermann H₂♂
Das Problem steckt meistens zwischen den Ohren
DRY DRY KISS
H₂ (wenn bei meinen Snipplets nichts anderes angegeben ist Lizenz: WTFPL)
  Mit Zitat antworten Zitat
Perlsau
(Gast)

n/a Beiträge
 
#7

AW: Record-Variable in Klasseninstanz setzen

  Alt 9. Dez 2012, 17:09
Wird denn die Variable von TMain_Zugriff User_Tab : TTab_User; initialisiert?
Inzwischen befindet sich die Variable nicht mehr in TMain_Zugriff, sondern in TDatMod (Datenmodul). Die Initialisierung hat sich jedoch nicht geändert, sie erfolgt nach wie vor aus der Unit MainTools heraus (mit DatMod. statt wie zuvor mit MZGF.), die alle Datenbank-Aktionen des Hauptformulars übernimmt:

Delphi-Quellcode:
procedure TMainTools.Einstellungen_Setzen;
CONST
     N = '0';
VAR
   i,z,
   Links,
   Oben,
   Breit,
   Hoch : Integer;
   Maximiert : Boolean;
   Aus : String;

begin
     IF NOT DatMod.Aset_Benutzer.Active THEN EXIT;

     Rec_User.User_Index := DatMod.Aset_Benutzer.FieldByName('Idx_Benutzer').AsInteger;
     Rec_User.Name := DatMod.Aset_Benutzer.FieldByName('Name').AsString;
     Rec_User.Pass := DatMod.Aset_Benutzer.FieldByName('Passwort').AsString;

     Links := DatMod.Aset_Benutzer.FieldByName('Links').AsInteger;
     Oben := DatMod.Aset_Benutzer.FieldByName('Oben').AsInteger;
     Breit := DatMod.Aset_Benutzer.FieldByName('Breit').AsInteger;
     Hoch := DatMod.Aset_Benutzer.FieldByName('Hoch').AsInteger;
     Maximiert := DatMod.Aset_Benutzer.FieldByName('Maximiert').AsBoolean;

     MZGF.Set_FormMain(Links, Oben, Breit, Hoch, Maximiert);

     IF NOT DatMod.Aset_UserTabs.Active THEN EXIT;
     IF NOT DatMod.Aset_UserTabs.Locate('Idx_User_Tabs',Rec_User.User_Index,[]) THEN EXIT;

// Filmtitel - Initialisierung von User_Tab
     DatMod.User_Tab.Titel_Index := DatMod.Aset_UserTabs.FieldByName('Titel_Index').AsInteger;
     DatMod.User_Tab.Titel_SortIndex := DatMod.Aset_UserTabs.FieldByName('Titel_SortIndex').AsInteger;
     DatMod.User_Tab.Titel_SortOrder := NOT DatMod.Aset_UserTabs.FieldByName('Titel_SortOrder').AsBoolean;
     DatMod.User_Tab.Titel_SortText := DatMod.Aset_UserTabs.FieldByName('Titel_SortText').AsString;

     DatMod.Aset_Titel.Locate('Idx_Titel',DatMod.User_Tab.Titel_Index,[]);
     Sortieren_FilmTitel(DatMod.User_Tab.Titel_SortIndex);

     z := MZGF.Spalten_Filmtitel -1;
     FOR i := 0 TO z DO
     BEGIN
          Aus := IntToStr(i);
          IF i < 10 THEN Aus := N + Aus;
          Aus := 'Tab_Titel_' + Aus;
          MZGF.Set_Spalte_FilmTitel(i,DatMod.Aset_UserTabs.FieldByName(Aus).AsInteger);
     END;
end;
Was ich nicht verstanden hatte: Wieso funktionierte ein ähnliches Konstrukt in einer anderen Klasse (und Unit) vom Hauptformular aus und das im ersten Post gezeigte Konstrukt vom Datenmodul aus nicht?

Das AfterScroll-Ereignis im Datenmodul wird bereits vor dem Setzen der Werte in MainTool ausgelöst, nach dem ersten Dataset.Open (korrekt: DatMod.User_Tab.Active := true) wird es garantiert ausgelöst – so dürfte der nicht-initialisierte Zustand der Variablen aber dennoch keine Rolle spielen, da der Wert der Variablen ja nicht ausgelesen, sondern gesetzt wird ... Das war es doch, worauf deine Frage zielte, oder?

Ich glaub ich muß für heute mal aufhören, sitze schon seit 5:30 hier ...
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
11.009 Beiträge
 
Delphi 12 Athens
 
#8

AW: Record-Variable in Klasseninstanz setzen

  Alt 9. Dez 2012, 17:58
Außerdem würden, wenn deine Vermutung zuträfe, auch die anderen Proceduren wie z.B. Set_StatusBar, die ja funktionieren, eine Zugriffsverletzung auslösen.
Nicht zwingend! Solange diese Methoden keine Felder der Instanz ansprechen, funktionieren die auch wenn die Instanz nicht initialisiert ist. (Wobei man sich dann aber fragen muss, warum sie nicht als Klassenmethoden realisiert sind bzw. überhaupt zu der Klasse gehören.)
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
Perlsau
(Gast)

n/a Beiträge
 
#9

AW: Record-Variable in Klasseninstanz setzen

  Alt 9. Dez 2012, 18:45
Außerdem würden, wenn deine Vermutung zuträfe, auch die anderen Proceduren wie z.B. Set_StatusBar, die ja funktionieren, eine Zugriffsverletzung auslösen.
Nicht zwingend! Solange diese Methoden keine Felder der Instanz ansprechen, funktionieren die auch wenn die Instanz nicht initialisiert ist. (Wobei man sich dann aber fragen muss, warum sie nicht als Klassenmethoden realisiert sind bzw. überhaupt zu der Klasse gehören.)
Set_StatusBar gehört zur Klasse TMain_Zugriff, wie du im ersten Code-Fenster meines ersten Postings leicht feststellen kannst ...
  Mit Zitat antworten Zitat
Bjoerk

Registriert seit: 28. Feb 2011
Ort: Mannheim
1.384 Beiträge
 
Delphi 10.4 Sydney
 
#10

AW: Record-Variable in Klasseninstanz setzen

  Alt 9. Dez 2012, 21:35
Zeig mal den constructor von TMain_Zugriff.

Probier eventl. mal das. Wenn das funzt liegt Uwe wohl richtig:

Delphi-Quellcode:
procedure TDatMod.DataModuleCreate(Sender: TObject);
begin
  Aset_TitelAfterScrollFlag:= false;
  MainZu := TMain_Zugriff.Create;
  Aset_TitelAfterScrollFlag:= true;
end;

procedure TDatMod.Aset_TitelAfterScroll(DataSet: TDataSet);
begin
   if Aset_TitelAfterScrollFlag then
   begin
     ..
   end;
end;
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 2  1 2      


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 18:47 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