Autor Beitrag
Thjostalf
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 21

Win2k, WinXP
D3 Prof, D4 Prof, Turbo Delphi Prof
BeitragVerfasst: Mi 12.04.06 10:55 
Hallo,

ich versuche mich gerade an der Umsetzung des folgenden Problems:

Ich habe eine Superklasse, die Methoden zum Laden uns Speichern bereitstellt. Davon abgeleitet gibt es momentan eine, zukünftig (wenn's denn klappt) mehrere Tochterklassen, die hauptsächlich als Datencontainer fungieren sollen, sprich einfache Datentypen aufnehmen sollen.

Jetzt wollte ich nicht in jeder Tochterklasse eine Methode "save" anlegen müssen, sondern ich will, daß das Speichern und Laden primär über die Superklasse läuft.

Leider springt mir immer eine Exception entgegen, wenn ich die published Properties der Subklasse ermitteln möchte. Nehme ich statt dessen irgendein Objekt einer anderen Klasse, z.B. TButton, funktioniert's.

Weiß jemand einen Rat?

Vereinfacht mein Sourcecode:

Superklasse
ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
unit uParameterSet;

interface

type
  TParameterSet = class(TObject)
  private
  public
    procedure Save;
  end;

implementation

uses
  Classes, Windows, SysUtils, Controls, Forms, TypInfo, Dialogs, ApplicationWindow, StdCtrls;

procedure TParameterSet.Save();
var
 plList: tPropList;
 iTest: integer;
 Obj: TButton;
begin

  Obj:= TButton.Create(nil);
  MessageDlg(Self.ClassName, mtInformation, [mbOK], 0); //Gibt TParmSetTest aus, wenn von einer Instanz von TParmSetTest aus aufgerufen

//--> Hier hängt's: Obj.ClassInfo funktioniert, Self.ClassInfo wirft die Exception
    iTest:= GetPropList(PTypeInfo(Self.ClassInfo),
    [tkUnknown, tkVariant, tkInteger, tkFloat,
    tkString, tkWString, tkLString, tkChar, tkWChar,
    tkEnumeration, tkSet, tkClass, tkMethod, tkArray,
    tkRecord, tkInterface], @plList);

  MessageDlg(IntToStr(iTest), mtInformation, [mbOK], 0);

  Obj.Free();
end;

end.


Tochterklasse
ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
unit uParmSetTest;

interface

uses
  uParameterSet;

type
  TParmSetTest = class(TParameterSet)
  private
    PTest: string;
    function GetTest: string;
    procedure SetTest(aString: string);
  published
    property Test: string read GetTest write SetTest;
  end;

implementation

uses
  Forms, SysUtils;

(* (TParmSetTest) *************************************************************)
(* (private) Liefert den Wert der Instanzvariablen PTest                      *)
(******************************************************************************)
function TParmSetTest.GetTest(): string;
begin
  Result:= PTest;
end;

(* (TParmSetTest) *************************************************************)
(* (private) Setzt den Wert der Instanzvariablen PTest                        *)
(******************************************************************************)
procedure TParmSetTest.SetTest(aString: string);
begin
  PTest:= aString;
end;
end;
BenBE
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 8721
Erhaltene Danke: 191

Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
BeitragVerfasst: Mi 12.04.06 11:09 
Leite deine Hauptklasse von TPersistent ab und deklariere alle zu speichernden Werte als published-Properties. Danach kannst Du die Daten über TStream.WriteComponent bzw. TStream.Readcomponent (IIRC) schreiben bzw. lesen.

Anzumerken sei an dieser Stelle auch, dass Delphi für jede Klasse mit Compiler-Status {$H+} Laufzeittyp-Informationen generiert, die man für diesen Zweck auswerten kann (siehe TypeInfo). Der Zugriff auf diese und deren Verarbeitung ist aber das Grauen schlechthin für ungeübte User ...

_________________
Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
Thjostalf Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 21

Win2k, WinXP
D3 Prof, D4 Prof, Turbo Delphi Prof
BeitragVerfasst: Mo 17.04.06 19:44 
Titel: Die Lösung
Hallo,

ich habe für mich eine Lösung gefunden. Für den Fall, daß noch jemand den gleichen Gedanken verfolgen sollte, folgt hier der Code der Basisklasse...

Die Unit uRegistryTools und die daraus verwendeten Klassenmethoden kapseln nur etwas den Registry-Zugriff, so daß ein vereinfachter Zugriff auf HKCU\Software\Company\Application besteht. Die benötigten Informationen werden aus den Programmeigenschaften ermittelt.
Die 3 Stellen müßte man also noch etwas umschreiben oder bei mir den entsprechenden Code erfragen.

Grüße - und danke für TPersistent, Marian

ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
(*******************************************************************************
 *
 * Unit Name: uParameterSet
 * Purpose  : Grundlage für Einstellungen / Programmparameter
 *            Liest und schreibt published Properties (einfache Datentypen
 *            wie Integer, Float und String) abgeleiteter Klassen
 * Author   : Marian Schmöker
 * History  : 1.0.6.417: Lesen und Schreiben von published Properties in der Registry (HKCU)
 *
 ******************************************************************************)


unit uParameterSet;

interface

uses
  Classes;

type
  TParameterSet = class(TPersistent)
  public
    procedure LoadFromUserRegistry;
    procedure SaveToUserRegistry;
  end;

implementation

uses
  SysUtils, TypInfo,
  uRegistryTools;

(* (TParameterSet.LoadFromUserRegistry) ****************************************
 * (public) Lädt die Werte der Properties aus der Registry unter
 *          HKCU\Software\Company\Application
 *          und dem entsprechenden Name der Eigenschaft
 ******************************************************************************)

procedure TParameterSet.LoadFromUserRegistry();
var
  iProp: Integer;
  PropCount: Integer;
  PropList: PPropList;
  PropInfo: PPropInfo;
  PropName: string;
  PTInfo: PTypeInfo;
  PTData: PTypeData;
  vValue: Variant;
begin
  PTInfo:= Self.ClassInfo;
  PTData:= GetTypeData(PTInfo);
  GetMem(PropList, PTData^.PropCount * SizeOf(PPropInfo));

  try
    TRegistryTools.OpenRegistry();
    PropCount:= GetPropList(PTInfo, [tkFloat, tkInteger, tkString, tkLString, tkWString ], PropList);

    for iProp:= 0 to PropCount - 1 do
    begin
      PropName:= PropList^[iProp]^.Name;
      PropInfo:= GetPropInfo(PTInfo, PropName);

      if PropInfo <> nil then
      begin
        (* Wert der Eigenschaft immer als Zeichenfolge aus Registry lesen!
         * Umwandlung erfolgt dann bei der Zuweisung der Eigenschaft *)

        vValue:= TRegistryTools.ReadRegistryStringEntry(PropName);
        case PropInfo^.PropType^.Kind of
          tkFloat: SetFloatProp(Self, PropInfo, VarAsType(vValue, varDouble));
          tkInteger: SetOrdProp(Self, PropInfo, VarAsType(vValue, varInteger));
          tkString, tkLString, tkWString: SetStrProp(Self, PropInfo, VarAsType(vValue, varString));
        end;
      end;
    end;
  finally
    FreeMem(PropList, PTData^.PropCount * SizeOf(PPropInfo));
    TRegistryTools.CloseRegistry();
  end;
end;

(* (TParameterSet.SaveToUserRegistry) ******************************************
 * (public) Speichert die Properties in der Registry unter
 *          HKCU\Software\Company\Application
 *          und dem entsprechenden Name der Eigenschaft
 ******************************************************************************)

procedure TParameterSet.SaveToUserRegistry();
var
  iProp: Integer;
  PropCount: Integer;
  PropList: PPropList;
  PropInfo: PPropInfo;
  PropName: string;
  PTInfo: PTypeInfo;
  PTData: PTypeData;
  vValue: Variant;
begin
  PTInfo:= Self.ClassInfo;
  PTData:= GetTypeData(PTInfo);
  GetMem(PropList, PTData^.PropCount * SizeOf(PPropInfo));

  try
    TRegistryTools.OpenRegistry();
    PropCount:= GetPropList(PTInfo, [tkFloat, tkInteger, tkString, tkLString, tkWString ], PropList);

    for iProp:= 0 to PropCount - 1 do
    begin
      PropName:= PropList^[iProp]^.Name;
      PropInfo:= GetPropInfo(PTInfo, PropName);

      if PropInfo <> nil then
      begin
        case PropInfo^.PropType^.Kind of
          tkFloat: vValue:= GetFloatProp(Self, PropInfo);
          tkInteger: vValue:= GetOrdProp(Self, PropInfo);
          tkString, tkLString, tkWString: vValue:= GetStrProp(Self, PropInfo);
        end;
        (* Wert der Eigenschaft immer als Zeichenfolge in Registry schreiben!
         * Typen gibt's da eh kaum... *)

        TRegistryTools.WriteRegistryStringEntry(PropName, VarAsType(vValue, varString));
      end;
    end;
  finally
    FreeMem(PropList, PTData^.PropCount * SizeOf(PPropInfo));
    TRegistryTools.CloseRegistry();
  end;
end;

end.