Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   INI File Schreiben und auslesen (https://www.delphipraxis.net/179542-ini-file-schreiben-und-auslesen.html)

floppybe 13. Mär 2014 19:44

INI File Schreiben und auslesen
 
Liebes Forum,

als Neuling mit Delphi versuche ich es bei Euch um eine Antwort zu bekommen was ich falsch mache.
Ich möchte in eine INI Datei die letzte Eingestellte Sprache eintragen und weis leider beim besten
willen nicht wie ich das gebacken kriege.

Ich habe dsa TUT bem Delphi-Treff genommen und adaptiert.
Ich habe auch dort schon einen Beitrag um Hilfe geschrieben. Leider aber nicht die Hilfe bekommen
die ich mir erhoffte.

Ich habe folgenden Code geschrieben um die INI Datei zu schreiben:

Delphi-Quellcode:
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
 const
  Sektion='language';
  Eintrag='last language';
var
  ini: TIniFile;
  filename: String;

begin
  SiLang1.Language := ComboBox1.Items.Strings[ ComboBox1.ItemIndex ];

filename := ExtractFilePath(ParamStr(0)) + './ini/einstellungen.ini'; //???
  ini := TIniFile.Create(filename);
  try
  ini.WriteString(Sektion,Eintrag,ComboBox1.Text);

  finally
Ini.Free;
  end;
und den Code um die Sprache wieder zum auslesen:

Delphi-Quellcode:
procedure TForm1.FormCreate(Sender: TObject);
const
 Sektion='language';
 Eintrag='last language';
var
  ini: TIniFile;
  filename: String;
begin

  filename := ExtractFilePath(ParamStr(0)) + './ini/einstellungen.ini'; //???
  ini := TIniFile.Create(filename);

  ComboBox1.Text := Ini.ReadString('Sektion', 'Eintrag', 'Wert');
finally
  ini.Free;
Bitte macht mich nicht fertig wenn ich es nicht verstehe, aber bei mir funktioniert es nicht.

Kann mir jemand von Euch hier aus dem Forum etwas behilflich sein ? Ich wäre sehr dankbar.

Herzliche Grüsse aus der Schweiz

Chris

Bjoerk 13. Mär 2014 19:57

AW: INI File Schreiben und auslesen
 
Delphi-Quellcode:
filename := ExtractFilePath(ParamStr(0)) + 'ini\einstellungen.ini';

floppybe 13. Mär 2014 20:08

AW: INI File Schreiben und auslesen
 
Vielen Dank Björk,

ok, dann muss ich also doch nicht "./" eingeben.

Zitat:

Zitat von Bjoerk (Beitrag 1251923)
Delphi-Quellcode:
filename := ExtractFilePath(ParamStr(0)) + 'ini\einstellungen.ini';

Ich bin noch am versuchen die INI auszu lesen, das klappt nun aber leider noch gar nicht,
ich denke ich mache im Code:

Delphi-Quellcode:
ComboBox1.Text := Ini.ReadString('Sektion', 'Eintrag', 'Wert');
irgend was falsch.

Es steht nun in der INI Datei "language" als Sektion 1 und in Sektion 2 steht nun der Wert "last language"
Und als Wert steht nun Englisch oder Deutsch oder so. Aber wenn ich die Applikation starte steht da immer
nur Wert in der Combobox.

Leider verstehe ich nicht so viel dass ich mir den Code einfach zusammenbauen kann.
eigentlich möchte ich ja schreiben aus Eintrag Sektion1 (Sektion) und Sektion 2 (Eintrag) den Wert,
ausgeben in Combobox1.Text. Das steht nun bei mir aber immer "Wert"

Danke dass Du Dir für mich Zeit nimmst.

Chris

Bjoerk 13. Mär 2014 20:14

AW: INI File Schreiben und auslesen
 
Hallo Chris,
bist du mit Verzecihnis C:\...\ini\ sicher ?

BTW:
Inifile Format:
[Section]
Indent = Value

Ini.WriteInteger('A','B',1) ergibt:

[A]
B = 1

zeras 13. Mär 2014 20:22

AW: INI File Schreiben und auslesen
 
Delphi-Quellcode:
filename := ExtractFilePath(ParamStr(0)) + 'ini\einstellungen.ini';
bedeutet, dass du möglicherweise in ein Unterverzeichnis des Programme Ordners schreiben willst, was in der Regel nicht geht, wenn man kein Admin ist.

Delphi-Quellcode:
ComboBox1.Text := Ini.ReadString('Sektion', 'Eintrag', 'Wert');
bedeutet, dass du den Text der Combobox mit dem Text 'Wert' beschreiben willst. Das soll doch nicht deine Absicht sein.
Du willst doch bestimmt, dass die Comboxbox mehrere Einträge hat und ein Eintrag gewählt wird. Das machst du mit "IndexOf".

Popov 13. Mär 2014 20:29

AW: INI File Schreiben und auslesen
 
Beispiel:

Delphi-Quellcode:
uses
  IniFiles;

procedure TForm1.Button1Click(Sender: TObject);
var
  Path, FileName: String;
  IniFile: TIniFile;
  s: String;
begin
  Path := ExtractFilePath(ParamStr(0)); //hat schon ein Backslash am Ende
  Path := IncludeTrailingPathDelimiter(Path); //braucht man hier nicht, wenn man sich aber unsicher ist, ist es nicht falsch
  FileName := 'Einstellungen.ini';

  //Erstellt falls nötig den Unterordner
  if not ForceDirectories(Path + 'INI') then
  begin
    MessageDlg('Es wurde kein Unterordner angelegt', mtError, [mbOK], 0);
    Exit;
  end;

  //Alternative: Prüft auf Unterordner
  {if not DirectoryExists(Path + 'INI') then
  begin
    MessageDlg('Es wurde kein Unterordner gefunden', mtError, [mbOK], 0);
    Exit;
  end; }

  IniFile := TIniFile.Create(Path + FileName);
  try
    IniFile.WriteString('Sektion', 'Ident', 'Wert');

    s := IniFile.ReadString('Sektion', 'Ident', 'Ersatzwert bei Fehler');
  finally
    IniFile.Free;
  end;

  ShowMessage(s);
end;
Ini ist eine ganz normale Klasse, sie unterschiedet sich von den meisten vielleicht dadurch, dass man in Create ein Pfad angeben muss. Experimentiere nicht mit dem Pfad, sondern liefere etwas mit Hand und Fuß. Wenn du keine Ahnung hast was für ein Pfad du angibst, dann weißt du auch nicht wo die Ini landet.

Das sieht nicht gut aus:
Delphi-Quellcode:
ExtractFilePath(ParamStr(0)) + './ini/einstellungen.ini';
ExtractFilePath liefert immer ein Backslash am Ende, also z. B. c:\Temp\Übungen\Projekt1\. Du brauchst also nach ExtractFilePath keinen Backslash angeben (schon gar nicht ein Slash).

Bist du nicht sicher ob am Ende ein Backslash ist, überprüfe es oder verwende die Funktion
Delphi-Quellcode:
IncludeTrailingPathDelimiter
. Sie macht nichts kaputt, man kann sie zur Not also immer verwenden. Wenn da bereits ein Backslash am Ende ist, wird nichts gemacht, sonst Backslash angefügt.

Willst du es in einen Ordner schrieben, dann in der Form:
Delphi-Quellcode:
ExtractFilePath(ParamStr(0)) + 'ini/einstellungen.ini';
. Wie gesagt, ExtractFilePath liefert am ende immer einen Backslash.
Fehlt der Ordner "Ini\", gibt es eine Fehlermeldung. Also vorher vergewissern ob der Ordner existiert. Oder mit
Delphi-Quellcode:
ForceDirectories
erstellen.

Der Rest ist üblich wie bei anderen Klassen: Objekt deklarieren und erstellen, mit Objekt arbeiten, zuletzt IniFile Objekt freigeben.

nuclearping 13. Mär 2014 20:49

AW: INI File Schreiben und auslesen
 
Das was du in '...' setzt, wird von Delphi auch als String interpretiert. Also hat er in der Ini eine Sektion "Sektion" gesucht, um dort den Eintrag "Eintrag" lesen zu können.
Wenn du die Konstanten (
Delphi-Quellcode:
const Sektion='language'; ...
) verwenden willst, dann darfst du das nicht in '...' setzen.

Delphi-Quellcode:
procedure TForm1.FormCreate(Sender: TObject);
const
 Sektion='language';
 Eintrag='last language';
var
  ini: TIniFile;
  filename: String;
begin

  filename := ExtractFilePath(ParamStr(0)) + '\ini\einstellungen.ini'; //???
  ini := TIniFile.Create(filename);

  // ComboBox1.Text := Ini.ReadString('Sektion', 'Eintrag', 'Wert');
  ComboBox1.Text := Ini.ReadString(Sektion, Eintrag, 'Standardwert');
finally
  ini.Free;

floppybe 13. Mär 2014 21:05

AW: INI File Schreiben und auslesen
 
Hallo Björk, nuclearping, Popov, und alle die Mit Lesen,

Ja, es ist eigentlich so angedacht, dass ich das Programm frei
wählbar Installieren möchte, das bedeutet, dass es überall
geladen werden kann so auch die INI Datei, deshalb keinen
Direkten Pfad.

Ich habe gelesen dass mann über den Parameter:
Delphi-Quellcode:
ExtractFilePath(ParamStr(0))
den Programm Ordner
auslesen kann, und dann soll es einfach ins Verzeichnis INI abgelegt
werden.

Nun ja, ich möchte nicht nur den Wert aus der Combobox verwenden,
den habe ich gefunden in:
Delphi-Quellcode:
ComboBox1.Items
sondern ich möchte den Wert aus der INI Datei in die Combobox
einlesen.

Ich habe ein Programm, dass ich Mehrsprachig machen möchte.

Grüsse all denen die mir da so tatkräftig Helfen.

Chris

Popov 13. Mär 2014 21:27

AW: INI File Schreiben und auslesen
 
Hier noch ein Tipp. Delphi bietet für Ini Nutzer den einfachen Zugriff auf Registry über die TRegIniFile. Die funktioniert genauso wie die TIniFile. Es muss einfach nur die Unit und die Klasse getauscht werden. Der Rest bleibt. Was also bei Ini funktionierte, funktioniert dann auch in der Registry, mit dem Unterschied, dass man nun keine Ini mitschleppten muss.

Delphi-Quellcode:
uses
  Registry; //Änderung Registry

procedure TForm1.Button2Click(Sender: TObject);
var
  Path: String;
  IniFile: TRegIniFile; //Änderung TRegIniFile
  s: String;
begin
  //Dem Pfad sollte 'Software\' vorangestellt sein, damit es richtig im Ordner
  //für Software abgeleht wird. Dann noch einen Dateinamen. Der Rest wie bei Ini.
  Path := 'Software\' + 'MeinProgramm';

  //Alternativ einfach den Namen der Datei nehmen
  //Path := 'Software\' + ExtractFileName(ChangeFileExt(ParamStr(0), ''));

  IniFile := TRegIniFile.Create(Path); //Änderung TRegIniFile
  try
    IniFile.WriteString('Sektion', 'Ident', 'Wert');

    s := IniFile.ReadString('Sektion', 'Ident', 'Ersatzwert bei Fehler');
  finally
    IniFile.Free;
  end;

  ShowMessage(s);
end;

Perlsau 13. Mär 2014 23:53

AW: INI File Schreiben und auslesen
 
Crosspost Delphitreff

Volker Z. 14. Mär 2014 01:00

AW: INI File Schreiben und auslesen
 
Hallo,

ich gebe Deinen Code jetzt mal in gekürzter Fassung wieder:
Delphi-Quellcode:
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
const
  Sektion='language';
  Eintrag='last language';
var
  ini: TIniFile;
begin
  ini := TIniFile.Create(filename);
  try
    ini.WriteString(Sektion,Eintrag,ComboBox1.Text);
  finally
    Ini.Free;
  end;
Delphi-Quellcode:
procedure TForm1.FormCreate(Sender: TObject);
var
  ini: TIniFile;
begin
  ini := TIniFile.Create(filename);
  try
    ComboBox1.Text := Ini.ReadString('Sektion', 'Eintrag', 'Wert');
  finally
    ini.Free;
  end
end;
Aua!

Dein FormClose schreibt:
Code:
[language]
last language=das was in ComboBox1.Text steht
Dein FormCreate ließt (bzw. möchte lesen):
Code:
[Sektion]
Eintrag=
Da vermutlich weder der Abschnitt Sektion, noch der Schlüssel Eintrag existiert bzw. dem Schlüssel Eintrag kein Wert zugewiesen ist, steht am Ende der string 'Wert' in ComboBox1.Text drin - und das ist dann schon richtig so.

Versuche es mal so (das mit dem filename musst Du halt noch ergänzen):
Delphi-Quellcode:
const
  Sektion = 'language';
  Eintrag = 'last language';

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
var
  ini: TIniFile;
begin
  ini := TIniFile.Create(filename);
  try
    ini.WriteString (Sektion, Eintrag, ComboBox1.Text);
  finally
    Ini.Free
  end;

procedure TForm1.FormCreate(Sender: TObject);
var
  ini: TIniFile;
begin
  ini := TIniFile.Create(filename);
  try
    ComboBox1.Text := Ini.ReadString (Sektion, Eintrag, 'Default');
  finally
    ini.Free
  end
end;
[EDIT]
Scheinbar ändert sich filename in Deiner Anforderung nicht; deshalb wäre es sinnvoll, dass Du Dir diese Geschichte nur einmal (FormCreate) zusammenschraubst und anschließend nur noch darauf zugreifst. Also:
Delphi-Quellcode:
type
  TForm1 = class (TForm)
  private
    FIniFileName : string;
  end;

procedure TForm1.CloseForm (Sender : TObject);
begin
  ini := TIniFile.Create (FIniFileName);

  // Rest wie gehabt
end;

procedure TForm1.CreateForm (Sender : TObject);
begin
  FIniFileName := ExtractFilePath (ParamStr (0)) + 'ini\einstellungen.ini';
 
  // Rest wie gehabt
end;
[/EDIT]

Gruß

floppybe 14. Mär 2014 01:51

AW: INI File Schreiben und auslesen
 
Hallo Perlsau,

Zitat:

Zitat von Perlsau (Beitrag 1251949)

Das hast Du aber Gut erkannt, ich habe es ja schon geschrieben.
Darf man etwas anständige Antworten erwarten wenn man nicht mehr weiter weis ?

Gruss

Chris

floppybe 14. Mär 2014 02:02

AW: INI File Schreiben und auslesen
 
Hallo Volker Z.,

ein Dankeschön dass Du Dir Zeit nimmst für mein Problem.
In der INI Steht nun:

[language]
last language=Französisch

Ich verstehe nun das nicht, wie der Eintrag "Französisch"
wieder in die Combobox zurück kommt.

Ich habe nun also genau den Wert den ich brauche.
Nur das mit dem Auslesen verstehe ich nicht.

Herzlichen Dank allen die da mir das ganze näher Bringen.

Chris


Zitat:

Zitat von Volker Z. (Beitrag 1251952)
Hallo,

ich gebe Deinen Code jetzt mal in gekürzter Fassung wieder:

Dein FormCreate ließt (bzw. möchte lesen):
Code:
[Sektion]
Eintrag=
Da vermutlich weder der Abschnitt Sektion, noch der Schlüssel Eintrag existiert bzw. dem Schlüssel Eintrag kein Wert zugewiesen ist, steht am Ende der string 'Wert' in ComboBox1.Text drin - und das ist dann schon richtig so.



Gruß


Volker Z. 14. Mär 2014 02:58

AW: INI File Schreiben und auslesen
 
Hallo,

nochmal: Beim Schreiben der Daten in die INI nutzt Du die Konstante
Delphi-Quellcode:
Sektion='language';
; Du legst also im Abschnitt 'language' den Schlüssel 'last language' an und hinterlegst dort einen bestimmten Wert ('Französisch'). Soweit passt alles.

Beim Lesen aus der INI greifst Du aber auf den Abschnitt 'Sektion' zu - und eben nicht auf 'language'. Oder: Du liest etwas, was gar nicht 'da' ist.

Ein
Delphi-Quellcode:
ComboBox1.Text := Ini.ReadString ('language', 'last language', 'Default');
würde funktionieren, ein
Delphi-Quellcode:
ComboBox1.Text := Ini.ReadString ('Sektion', 'Eintrag', 'Wert');
wird es nicht tun.

Deshalb: Nimm die Konstanten aus
Delphi-Quellcode:
FormClose
und
Delphi-Quellcode:
FormCreate
raus, definiere Dir die Dinger "global" und greife beim Lesen/Schreiben auf diese Konstanten zu.

Groschen nun gefallen?

Gruß

Popov 14. Mär 2014 07:48

AW: INI File Schreiben und auslesen
 
Klein wenig übertrieben der Code, aber es soll ja ein Beispiel sein:
Delphi-Quellcode:
uses
  IniFiles;

const
  LangList = 'Deutsch, Englisch, Französisch, Spanisch'; //Mit Komma getrennte Liste
  IniLangSection = 'Sprachen';
  IniLangList = 'ListeSprachen';
  IniCurLang = 'AktuelleSprache';

function GetIniPath(var IniPath: String): Boolean;
const
  IniSubFolder = 'Ini\';
  IniFileName = 'Einstellungen.ini';
var
  Path: String;
begin
  Path := ExtractFilePath(ParamStr(0));
  IniPath := Path + IniSubFolder + IniFileName;

  Result := ForceDirectories(Path + IniSubFolder);
  if not Result then
    MessageDlg('Fehler. Es konnte kein Ini Ordner angelegt werden.', mtError, [mbOK], 0);
end;

procedure LoadFromIni(ComboBox: TComboBox);
var
  IniFile: TIniFile;
  IniPath: String;
  s: String;
  k: Integer;
begin
  if not GetIniPath(IniPath) then Exit;

  IniFile := TIniFile.Create(IniPath);
  try
    s := IniFile.ReadString(IniLangSection, IniLangList, LangList);
    ComboBox.Items.CommaText := s;
    k := IniFile.ReadInteger(IniLangSection, IniCurLang, 0);
    if (k > -1) and (k < ComboBox.Items.Count) then
      ComboBox.ItemIndex := k;
  finally
    IniFile.Free;
  end;
end;

procedure SaveToIni(ComboBox: TComboBox);
var
  IniFile: TIniFile;
  IniPath: String;
  s: String;
begin
  if not GetIniPath(IniPath) then Exit;

  IniFile := TIniFile.Create(IniPath);
  try
    IniFile.WriteString(IniLangSection, IniLangList, ComboBox.Items.CommaText);
    IniFile.WriteInteger(IniLangSection, IniCurLang, ComboBox.ItemIndex);
  finally
    IniFile.Free;
  end;
end;

procedure AddLanguage(ComboBox: TComboBox; Name: String);
begin
  ComboBox.Items.Add(Name);
end;

procedure TForm1.ButtonLoadClick(Sender: TObject);
begin
  LoadFromIni(ComboBox1);
end;

procedure TForm1.ButtonSaveClick(Sender: TObject);
begin
  SaveToIni(ComboBox1);
end;

procedure TForm1.ButtonAddClick(Sender: TObject);
var
  s: String;
begin
  s := InputBox('Sprache', 'Neue Sprache hinzufügen', '');
  if Trim(s) <> '' then
    AddLanguage(ComboBox1, s);
end;

Bjoerk 14. Mär 2014 08:31

AW: INI File Schreiben und auslesen
 
Würde das eher mit einem Aufzählungstypen und über die Eigenschaft ItemIndex machen?
Delphi-Quellcode:
unit MyUnit;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, IniFiles;

type
  TLanguage = (lgGerman, lgFrench, lgEnglich, lgSwissGerman);

const
  cLanguage: array [TLanguage] of string =
    ('Deutsch', 'Französisch', 'Englisch', 'Schweizerdeutsch');

type
  TMyForm = class(TForm)
    LanguageComboBox: TComboBox;
    procedure FormCreate(Sender: TObject);
    procedure LanguageComboBoxClick(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    procedure SetLanguageComboBoxItems;
  end;

var
  MyForm: TMyForm;

implementation

{$R *.dfm}

var
  Language: TLanguage;

function LanguageRange(Value: integer): boolean;
var
  A, B: integer;
begin
  A := Integer(Low(TLanguage));
  B := Integer(High(TLanguage));
  Result := (Value >= A) and (Value <= B);
end;

function IntToLanguage(Value: integer): TLanguage;
begin
  if LanguageRange(Value) then
    Result := TLanguage(Value)
  else
    raise Exception.Create('IntToLanguage: Invalid Paramter');
end;

function LanguageToInt(Value: TLanguage): integer;
begin
  Result := Integer(Value);
end;

function IniFileName: string;
var
  Path: string;
begin
  Path := ExtractFilePath(ParamStr(0)) + 'ini\';
  if not DirectoryExists(Path) then
    if not CreateDir(Path) then
      raise Exception.Create(Format('Cannot create folder %s', [Path]));
  Result := Path + 'Einstellungen.ini';
end;

function LoadLanguageFromIniFile: TLanguage;
var
  Ini: TMemIniFile;
begin
  Ini := TMemIniFile.Create(IniFileName);
  try
    Result := IntToLanguage(Ini.ReadInteger('Language', 'Last Language', 0));
  finally
    Ini.Free;
  end;
end;

procedure SaveLanguageToIniFile(Language: TLanguage);
var
  Ini: TMemIniFile;
begin
  Ini := TMemIniFile.Create(IniFileName);
  try
    Ini.WriteInteger('Language', 'Last Language', LanguageToInt(Language));
  finally
    Ini.UpdateFile;
    Ini.Free;
  end;
end;

procedure TMyForm.LanguageComboBoxClick(Sender: TObject);
begin
  if LanguageRange(LanguageComboBox.ItemIndex) then
    Language := IntToLanguage(LanguageComboBox.ItemIndex);
end;

procedure TMyForm.SetLanguageComboBoxItems;
var
  I: TLanguage;
begin
  LanguageComboBox.Items.Clear;
  LanguageComboBox.Style := csDropDownList;
  for I := Low(I) to High(I) do // oder for I := lgGerman to lgSwissGerman do
    LanguageComboBox.Items.Add(cLanguage[I]);
end;

procedure TMyForm.FormCreate(Sender: TObject);
begin
  SetLanguageComboBoxItems;
  Language := LoadLanguageFromIniFile;
  LanguageComboBox.ItemIndex := LanguageToInt(Language);
  ShowMessage(cLanguage[Language]);
end;

procedure TMyForm.FormDestroy(Sender: TObject);
begin
  SaveLanguageToIniFile(Language);
end;

procedure DoSomething;
begin
  if Language = lgEnglich then
    ShowMessage('I like Englich');
end;

procedure DoSomethingElse;
begin
  case Language of
    lgGerman:
    begin

    end;
    lgFrench:
    begin

    end;
    lgEnglich:
    begin

    end;
    lgSwissGerman:
    begin

    end;
  end;
end;

end.

Sir Rufo 14. Mär 2014 08:39

AW: INI File Schreiben und auslesen
 
@Popov

Hier würden sich Exceptions besser machen, denn nun gibt es auch einen Hinweis, warum der nicht erstellt werden konnte
Delphi-Quellcode:
function GetIniPath : string;
const
  IniSubFolder = 'Ini\';
  IniFileName = 'Einstellungen.ini';
var
  Path: String;
begin
  Path := ExtractFilePath(ParamStr(0));
  Result := Path + IniSubFolder + IniFileName;

  if not ForceDirectories( Path + IniSubFolder ) then
    RaiseLastOSError;
end;

procedure LoadFromIni(ComboBox: TComboBox);
var
  IniFile: TIniFile;
  s: String;
  k: Integer;
begin
  IniFile := TIniFile.Create( GetIniPath );
  try
    s := IniFile.ReadString(IniLangSection, IniLangList, LangList);
    ComboBox.Items.CommaText := s;
    k := IniFile.ReadInteger(IniLangSection, IniCurLang, 0);
    if (k > -1) and (k < ComboBox.Items.Count) then
      ComboBox.ItemIndex := k;
  finally
    IniFile.Free;
  end;
end;

procedure SaveToIni(ComboBox: TComboBox);
var
  IniFile: TIniFile;
  s: String;
begin
  IniFile := TIniFile.Create( GetIniPath );
  try
    IniFile.WriteString(IniLangSection, IniLangList, ComboBox.Items.CommaText);
    IniFile.WriteInteger(IniLangSection, IniCurLang, ComboBox.ItemIndex);
  finally
    IniFile.Free;
  end;
end;

user0815 14. Mär 2014 10:01

AW: INI File Schreiben und auslesen
 
Ich würde die INI nicht ins Programmverzeichnis "ExtractFilePath(ParamStr(0))" packen, sondern ein passendes Verzeichnis dafür nehmen Stichwort: .http://www.delphipraxis.net/153680-c...ml#post1041628

http://www.delphipraxis.net/158434-o...ml#post1082613

floppybe 14. Mär 2014 10:33

AW: INI File Schreiben und auslesen
 
Hallo Volker Z.,

leider übersteigt das ganze mein Verständnis, nein, ich habs nicht so ganz verstanden.
Dass ich es lieber Global mache verstehe ich noch, aber wesshalb dann statt "Eintrag"
direkt "language" setzen verstehe ich noch nicht so ganz.

Und wesshalb übernimmt er mir den Wert nicht aus der INI ? In der INI Stehts ja.
Ich habe nochmals gelesen im TUT,
Delphi-Quellcode:
Edit1.Text:=Ini.ReadString('Section', 'Schlüssel1', 'kein Wert');
"Kein Wert" ist ja ein direkter Eintrag und nicht der Wert aus der INI oder verstehe ich das Falsch ?
Was muss ich dann einfügen bei "Wert" resp. "kein Wert" dass es klappt ? Muss ich doch noch eine Const. für "Wert" hinterlegen ?

Aber ich habe leider völligen Bahnhof.
Sorry für meine lange Leitung.

Chris

Zitat:

Zitat von Volker Z. (Beitrag 1251957)
Hallo,

nochmal: Beim Schreiben der Daten in die INI nutzt Du die Konstante
Delphi-Quellcode:
Sektion='language';
; Du legst also im Abschnitt 'language' den Schlüssel 'last language' an und hinterlegst dort einen bestimmten Wert ('Französisch'). Soweit passt alles.

Beim Lesen aus der INI greifst Du aber auf den Abschnitt 'Sektion' zu - und eben nicht auf 'language'. Oder: Du liest etwas, was gar nicht 'da' ist.

Ein
Delphi-Quellcode:
ComboBox1.Text := Ini.ReadString ('language', 'last language', 'Default');
würde funktionieren, ein
Delphi-Quellcode:
ComboBox1.Text := Ini.ReadString ('Sektion', 'Eintrag', 'Wert');
wird es nicht tun.

Deshalb: Nimm die Konstanten aus
Delphi-Quellcode:
FormClose
und
Delphi-Quellcode:
FormCreate
raus, definiere Dir die Dinger "global" und greife beim Lesen/Schreiben auf diese Konstanten zu.

Groschen nun gefallen?

Gruß


Sir Rufo 14. Mär 2014 10:44

AW: INI File Schreiben und auslesen
 
@floppybe

Wenn du dem Nachbarn Meier (Ident) im Haus zu deiner Linken (Sektion) einen Apfel gibts (Value)
Delphi-Quellcode:
begin
  Ini.WriteString( 'HausLinks', 'Meier', 'Apfel' );
end;
und dann den Nachbarn Müller (Ident) im Haus zu deiner Rechten (Sektion) fragst, was du ihm gegeben hast
Delphi-Quellcode:
if Ini.ReadString( 'HausRechts', 'Müller', 'normal nix' ) = 'Apfel' then
  ShowMessage( 'Juhu, Apfel essen :-)' )
else
  ShowMessage( 'In die Röhre schauen :-(' );
erwartest du dann den Apfel (Value)?

Nö, du musst schon genau den fragen, dem du den Apfel gegeben hast
Delphi-Quellcode:
if Ini.ReadString( 'HausLinks', 'Meier', 'normal nix' ) = 'Apfel' then
  ShowMessage( 'Juhu, Apfel essen :-)' )
else
  ShowMessage( 'In die Röhre schauen :-(' );
Es ist auch immer hilfreich sich die Dokumentation durchzulesen (anschauen reicht nicht)
Delphi-Referenz durchsuchenTCustumIniFile.WriteString und Delphi-Referenz durchsuchenTCustomIniFile.ReadString
dann siehst du auch, wann bei
Delphi-Quellcode:
ReadString
der Default-Wert zurückgeliefert wird.

Jumpy 14. Mär 2014 10:46

AW: INI File Schreiben und auslesen
 
Bei
Edit1.Text:=Ini.ReadString('Section', 'Schlüssel1', 'kein Wert');

ist "kein Wert" sowas wie der Default-Wert, den die Funktion nimmt, wenn Sie nix anderes fidnet. Könntest du dann so machen:

Edit1.Text:=Ini.ReadString('Section', 'Schlüssel1', 'Deutsch');

oder wenn Edit1.Text schon beim Start Standardmäßig auf Deutsch steht dann:

Edit1.Text:=Ini.ReadString('Section', 'Schlüssel1', Edit1.Text);


Das entscheidende, was dir aber alle sagen wollen ist, dass das bei dir nicht Section und Schlüssel1 heißt sondern language und last language, somit:

Edit1.Text:=Ini.ReadString('language', 'last language', Edit1.Text);

oder halt mit Variablen:
Delphi-Quellcode:
var Sektion,Eintrag:String;
...
Sektion:='language';
Eintrag:='last language';
...
Edit1.Text:=Ini.ReadString(Sektion, Eintrag, Edit1.Text);

Popov 14. Mär 2014 11:32

AW: INI File Schreiben und auslesen
 
@floppybe

Nochmal zum Aufbau einer Ini-Datei
Code:
[Sektion1]
Itent1=Value1
Ident2=Value2
Ident3=Value3

[Sektion2]
Itent1=Value1
Ident2=Value2
Ident3=Value3

[SektionN]
Itent1=Value1
Ident2=Value2
Ident3=Value3
Wie du siehst ist die Ini in Sektionen unterteilt. Dadurch ist es möglich drei Mal den Ident mit dem gleichen Namen "Ident1" zu verwenden, ohne dass es zu Komplikationen kommt. Ohne Sektionen müsste man für jeden Ident einen anderen Namen nehmen.

Nun keine Gesetze, aber Regeln die das Leben erleichtern:

Es empfiehlt sich die Namen für Sektionen und Idents als Konstanten global zu nutzen. Grund: man kann sich immer leicht verschreiben. Ohne Konstanten fällt der Fehler nicht auf
Delphi-Quellcode:
IniFile.ReadInteger('Sprachen', 'AktueleSprache', 0);
. Hier fällt dem Compiler nicht auf, dass in 'AktueleSprache' ein L fehlt. Wenn man bei
Delphi-Quellcode:
IniFile.ReadInteger(IniLangSection, IniCurrLang, 0);
ein Fehler macht, fällt es auf, dass "IniCurrLang" mit zwei R geschrieben wurde, denn IniCurLang ist eine Konstante.

Das Gleiche gilt für den IniPfad. Wer den Pfad immer wieder aufs neue zusammensetzt, dem unterläuft garantiert irgendwann ein Fehler. Am besten den Namen übe reine Funktion holen.

Nun zu deiner Frage:
Code:
Ich habe nochmals gelesen im TUT, Edit1.Text:=Ini.ReadString('Section', 'Schlüssel1', 'kein Wert'); "Kein Wert" ist ja ein direkter Eintrag und nicht der Wert aus der INI oder verstehe ich das Falsch ?
'kein Wert' ist ein Defaultwert bei Read Methoden und kommt immer dann zum Einsatz wenn ein Fehler vorliegt. Ein Fehler kann immer dann vorliegen, wenn es z. B. keinen Eintrag gibt. Entweder fehlt die Sektion oder Ident, so dass an der Stelle keine Information vorliegt. Statt aber mit einer Fehlermeldung abzubrechen, wird ersatzweise der Defautwert genommen, in dem Fall 'kein Wert'. Ein weitere Grund für einen Fehler kann sein, dass zwar alles da ist, also Sektion und Ident, aber man einen Integer laden will und die Funktion erkennt, dass in der Ini als Wert ein String vorliegt (Selbst wenn es "1.0" ist).

Der Defaultwert ist also der Ersatz-Wert im Fall, dass ein Fehler vorkommt und der Wert nicht korrekt gelesen werden kann.

Popov 14. Mär 2014 11:41

AW: INI File Schreiben und auslesen
 
Zitat:

Zitat von Sir Rufo (Beitrag 1251973)
@Popov

Hier würden sich Exceptions besser machen, denn nun gibt es auch einen Hinweis, warum der nicht erstellt werden konnte

Besser schon, ich weiß nur nicht ob TE RaiseLastOSError verstehen würde. Evtl. hätte man
Delphi-Quellcode:
raise Exception.Create('Fehler. Es konnte kein Ini Ordner angelegt werden.');
nehmen können. In dem Fall könnte man auch ein String als Ergebnis liefern und dennoch eine eigene Fehlermeldung verfassen.

Sir Rufo 14. Mär 2014 12:13

AW: INI File Schreiben und auslesen
 
Zitat:

Zitat von Popov (Beitrag 1251995)
Zitat:

Zitat von Sir Rufo (Beitrag 1251973)
@Popov

Hier würden sich Exceptions besser machen, denn nun gibt es auch einen Hinweis, warum der nicht erstellt werden konnte

Besser schon, ich weiß nur nicht ob TE RaiseLastOSError verstehen würde. Evtl. hätte man
Delphi-Quellcode:
raise Exception.Create('Fehler. Es konnte kein Ini Ordner angelegt werden.');
nehmen können. In dem Fall könnte man auch ein String als Ergebnis liefern und dennoch eine eigene Fehlermeldung verfassen.

Du hast Recht, dass die Fehlermeldung ruhig eindeutiger/informativer sein sollte. Hier also mit der OS-Meldung und dem Verzeichnis:
Delphi-Quellcode:
  if not ForceDirectories( Dir ) then
    RaiseLastOSError( GetLastError, Format(' "%s"', [Dir] ) );
Zitat:

EOSError: Systemfehler. Code: 3.
Das System kann den angegebenen Pfad nicht finden "X:\Test\Peter"
Zitat:

EOSError: Systemfehler. Code: 161.
Der angegebene Pfadname ist ungültig "\\192.168.0.1\Data\Test\Peter"
Und das mit dem Verstehen ist ja auch nicht so schwer, wenn er einen Blick auf Delphi-Referenz durchsuchenRaiseLastOSError wirft

Popov 14. Mär 2014 15:29

AW: INI File Schreiben und auslesen
 
Zitat:

Zitat von Sir Rufo (Beitrag 1251997)
Und das mit dem Verstehen ist ja auch nicht so schwer, wenn er einen Blick auf Delphi-Referenz durchsuchenRaiseLastOSError wirft

Also gegen Raise an sich ist ja nichts zu sagen, auf der anderen Seite ist die Frage seit zwei Seiten - was ist bei Ini ein Defaultwert?

Außerdem, was interessiert den Endnutzer, oder gar den TE der ErrorCode 5. Das ist fast so gut wie die Frage nach dem Sinn des Lebens mit der Antwort 42. Es sollte also immer ein Text da stehen.

Wo wir aber dabei sind, ich kenne die Schreibweise
Delphi-Quellcode:
RaiseLastOSError( GetLastError, Format(' "%s"', [Dir] ) );
nicht, ist das neu > D7?

p80286 14. Mär 2014 15:47

AW: INI File Schreiben und auslesen
 
Zitat:

Zitat von Popov (Beitrag 1251942)
..., mit dem Unterschied, dass man nun keine Ini mitschleppten muss.

Zumindestens mir fällt es wesentlich einfacher mit "richtigen" Dateien umzugehen als in den Tiefen der Registry etwas zu überprüfen.
Und dann verbleibt der Schrott von allen möglichen Programmen auch noch da...
Man kann also durchaus geteilter Meinung über den Vorteil von INI-Dateien sein.

Zitat:

Zitat von Popov (Beitrag 1252028)
, auf der anderen Seite ist die Frage seit zwei Seiten - was ist bei Ini ein Defaultwert?

Hab ich etwas übersehen?
"Default" ist das was rauskommt wenn die INI aus welchen Gründen auch immer nichts liefert.




Gruß
K-H

Popov 14. Mär 2014 16:31

AW: INI File Schreiben und auslesen
 
Zitat:

Zitat von p80286 (Beitrag 1252033)
Zitat:

Zitat von Popov (Beitrag 1251942)
..., mit dem Unterschied, dass man nun keine Ini mitschleppten muss.

Zumindestens mir fällt es wesentlich einfacher mit "richtigen" Dateien umzugehen als in den Tiefen der Registry etwas zu überprüfen.
Und dann verbleibt der Schrott von allen möglichen Programmen auch noch da...
Man kann also durchaus geteilter Meinung über den Vorteil von INI-Dateien sein.

Du bist Programmierer, da sollte man die Registry positiv sehen. Die Aversion die einige hier (nicht nur du) gegen Einsatz von Registry haben, ist ja schon etwas beängstigend. Außerdem schreibt man nichts in die Tiefen der Registry, sondern nur in HKEY_CURRENT_USER, und da in den Zweig SOFTWARE (zumindest wenn man TRegIniFile mit dem Pfad nutzt). Das ist der Bereich für die Software, der ist extra dazu gedacht, dass man da seine Einstellungen speichert.

Natürlich hat die Ini seinen Vorteil, der Nachteil ist aber, dass man sie nicht überall speichern kann. Die Alternative mit dem Anwendungsdaten-Ordner ist ok, das Problem ist, dass man sich vorher immer erst verrenken muss um den Ordner zu finden. Selbst wenn man den Ordner nicht mit der Spezial-Ordner-Funktion sucht und die Umgebungsvariablen nutzt, es muss immer erst nachgeguckt werden wie der Code so ist.
Delphi-Quellcode:
var
  Str: array [0..1023] of Char;
  Len: Integer;
begin
  Len := ExpandEnvironmentStrings(PChar('%APPDATA%'), Str, 1024);
Kürzer geht es nicht, in der Regel ist der Code komplizierter, aber heute ist so ein schöner Tag, deshalb habe ich es einfach gehalten. Die dritte Alternative ist einfach die TIniFile gegen die TRegIniFile tauschen, und schon sind alle Probleme vergessen ;)

p80286 14. Mär 2014 17:44

AW: INI File Schreiben und auslesen
 
Zitat:

Zitat von Popov (Beitrag 1252037)
Außerdem schreibt man nichts in die Tiefen der Registry, sondern nur in HKEY_CURRENT_USER, und da in den Zweig SOFTWARE (zumindest wenn man TRegIniFile mit dem Pfad nutzt). Das ist der Bereich für die Software, der ist extra dazu gedacht, dass man da seine Einstellungen speichert.

Hat den Nachteil, daß man nicht für Mehrere Benutzer die gleichen Parameter hat, falls notwendig.

Zitat:

Zitat von Popov (Beitrag 1252037)
Natürlich hat die Ini seinen Vorteil, der Nachteil ist aber, dass man sie nicht überall speichern kann.

Zumindestens Software die vom Stick und/oder einem Netzlaufwerk gestartet wird ist mit der INI besser bedient.

Übrigens habe ich nichts gegen die Registry, die Idee ist gut, die Umsetzung allerdings teilweise grausam.

Gruß
K-H

DateTimeError 14. Mär 2014 22:35

AW: INI File Schreiben und auslesen
 
Jupp. Neue Programme entwickle ich erstmal "portable" mit INIs mittels globalem
Delphi-Quellcode:
INIFileName := ChangeFileExt(paramstr(0), '.ini');
im FormCreate. :oops:

Popov 14. Mär 2014 22:49

AW: INI File Schreiben und auslesen
 
Würde ich nicht machen. Besser dafür ist eine Funktion geeignet, auch wenn sie als Result den gleichen Wert liefert. Man hat dann immer die Möglichkeit den Pfad anzupassen.

DateTimeError 14. Mär 2014 23:15

AW: INI File Schreiben und auslesen
 
Zitat:

Zitat von Popov (Beitrag 1252067)
Würde ich nicht machen. Besser dafür ist eine Funktion geeignet, auch wenn sie als Result den gleichen Wert liefert. Man hat dann immer die Möglichkeit den Pfad anzupassen.

Meinst Du mich?

floppybe 16. Mär 2014 08:33

AW: INI File Schreiben und auslesen
 
Hallo Jumpy und Popov,

jetzt wird es mir auch wieder etwas klarer mit der INI. OK.
Ich mache es mal Global und versuche den Test wieder auszulesen.

Ich erlaube mir das Resultat hier wieder zu Präsentieren.

Habt vielen Dank für Alle Eure Ausführungen.


Chris

DeddyH 17. Mär 2014 10:00

AW: INI File Schreiben und auslesen
 
Ich hatte es im DT schon angedeutet, trotzdem scheint es trotz mehrfacher Hinweise auch in diesem Thread immer noch nicht angekommen zu sein. Was wird in diesem Testcode jeweils ausgegeben?
Delphi-Quellcode:
const
  Blubb = 'Hallo Welt';
begin
  ShowMessage('Blubb');
  ShowMessage(Blubb);
Wenn man das weiß, muss man sich nur das Schreiben und Lesen noch einmal ganz genau anschauen.

floppybe 17. Mär 2014 18:54

AW: INI File Schreiben und auslesen
 
Hallo Guten Abend,

ich habe den Code vom Einlesen der INI so umgeschrieben dass ich statt der Const
direkt den "Wert" genommen habe.

Delphi-Quellcode:
  filename := ExtractFilePath(ParamStr(0)) + 'ini/einstellungen.ini'; //???
  ini := TIniFile.Create(filename);

  silang1.Language := Ini.ReadString('language', 'last language', 'Deutsch' );
Wenn ich "language" und "last language" nehme gehts. Ich verstehe das noch nicht so ganz,
binn aber froh dass ich nun weiter gekommen bin.

Danke an alle welche geholfen haben bei der Lösung.

Chris

Popov 17. Mär 2014 19:54

AW: INI File Schreiben und auslesen
 
Man merkt, dass du gegen gute Tipps schon klein wenig abgeneigt bist. Es ist nun mal so, dass es die typischen Fehler gibt. Anscheinend möchtest du die selbst erleben. Kann man aber auch gut verstehen - no Risk, no Fun ;)

Du verstehst nicht was da passiert? Dabei ist das Ganze eigentlich nicht so schwer. Aber vielleicht sollte man nicht alles auf einmal durchnehmen. Wenn du Lust hast, können wir es noch mal in kleinen Schritten durchgehen:

silang1.Language := Ini.ReadString('language', 'last language', 'Deutsch' );

Was sagt dieser Parameter aus. Welchen Teil der Ini repräsentiert er?

floppybe 20. Mär 2014 05:01

AW: INI File Schreiben und auslesen
 
Hallo Popov,

vielen Hertzlichen Dank dass Du Dir Zeit nimmst mir das ganze etwas besser zu Erklähren. Ich nehme das Angebot gerne an.

Language ist der Einstig der Werte Wo die Daten abgelegt sind, denke ich.
Wenn ich also "Sektion" nehmen würde sucht er mir nach Sektion und nicht nach Language. Wenn ich das Richtig verstehe.

Ich bin es mir gewohnt immer noch den Code selber zusammen zu basteln, als dass ich fertige Infos bekomme. Was
ich sehr Gut verstehen kann aber bei mir manchmal eben zu verwirrungen führt. Ich bin froh, einen Code zu bekommen
den mann verstehen kann. Ich habe einfach bei Euren Ausführungen falsch gelesen und miss verstanden.


Gruss

Chris

Zitat:

Zitat von Popov (Beitrag 1252322)
Man merkt, dass du gegen gute Tipps schon klein wenig abgeneigt bist. Es ist nun mal so, dass es die typischen Fehler gibt. Anscheinend möchtest du die selbst erleben. Kann man aber auch gut verstehen - no Risk, no Fun ;)

Du verstehst nicht was da passiert? Dabei ist das Ganze eigentlich nicht so schwer. Aber vielleicht sollte man nicht alles auf einmal durchnehmen. Wenn du Lust hast, können wir es noch mal in kleinen Schritten durchgehen:

silang1.Language := Ini.ReadString('language', 'last language', 'Deutsch' );

Was sagt dieser Parameter aus. Welchen Teil der Ini repräsentiert er?


Popov 20. Mär 2014 10:01

AW: INI File Schreiben und auslesen
 
Gut, ich hab die Frage missverständlich gestellt, ist aber nicht schlimm.

silang1.Language := Ini.ReadString('language', 'last language', 'Deutsch' );

Eigentlich wollte ich wissen, in welchem Teil der Ini er sucht. Man muss sich zuerst über den Aufbau der Ini im klaren sein, man muss sich bewusst sein, dass es bei der Ini drei Schritte bis zu gesuchten Info gibt - zuerst muss man die Ini-Datei wählen, dann muss man innerhalb der Ini den Teilbereich (Section) wählen, zuletzt muss man den Schlüssel (Ident) wählen:
Code:
IniFile
     |
     +-Section1
     |   |
     |   +-Ident1 = Value
     |   +-Ident2 = Value
     |   +-Ident3 = Value
     |
     +-Section2
     |   |
     |   +-Ident1 = Value
     |   +-Ident4 = Value
     |   +-Ident5 = Value
     |
     +-Section3
Man kann übrigens auch eine normale Textdatei nehmen, sie über TStringLIst laden und Werte über Schlüssel suchen. Das kann jede StringList, sie kennt nur keine Sektionen. Das würde dann so aussehen:
Code:
TxtFile
     |
     +-Name1 = Value
     +-Name2 = Value
     +-Name3 = Value
     +-Name4 = Value
     +-Name5 = Value
     +-Name6 = Value
Die StrlingList hat dann also eine Liste in der Form:
Code:
Info1 = Wert A
Info2 = Wert B
Info1 = Wert C
Man kann hier also genauso an die Daten kommen wie bei einer Ini. Was ist aber der Unterschied? Die StringList kennt keine Unterteilung innerhalb der Liste. In der ganzen Liste müssen alle Schlüssel (also Ident oder Names) stets einmalig sein, sonst werden sie nicht gefunden. Auf den Wert in Zeile 3 kommt man bei einer StringList nie dran - Grund: sie benutzt als Schlussel die Bezeichnung "Info1", und das taucht in der Liste in der ersten Zeile bereits auf. Die dritte Zeile kann also nicht angesprochen werden.

Hier schlaft die Ini-Datei Abhilfe, denn sie kann die Datei noch ein weiteres Mal unterteilen, und zwar in Sektionen.
Code:
[Sektion1]
Info1 = Wert A
Info2 = Wert B

[Sektion2]
Info1 = Wert C
Und nun kann man an den Wert drann, man muss aber der Funktion zum auslesen der Info sagen an welche Info1 in der Liste will man. In welcher Sektion befindet es sich. Also schriebt man:
Delphi-Quellcode:
xyz := Ini.ReadString('Sektion2', 'Info1 ', 'Erstzwert');
//
Sektion unterteilt also die eine große Ini Liste mit Werten in kleine Teillisten, geordnet nach Themen
Delphi-Quellcode:
silang1.Language := Ini.ReadString('language', 'last language', 'Deutsch' );
//
Man kann sich die Sektion auch als Ordner vorstellen und die Idents als Blätter mit Infos. In einen Ordner kann man viel Blätter legen. In einen weiteren Ordner kann man andere Blätter legen.

floppybe 24. Mär 2014 11:11

AW: INI File Schreiben und auslesen
 
Hallo Popov,

ich glaube langsam verstehe ich den Bereich INI auch etwas besser.
Bei der TXT Datei statt der INI würde man also Nur Language wählen,
oder last language aber nicht beides.

Dass ich aber das ganze auch über eine TXT Datei fürhren könnte
wusste ich nun auch nicht. Ist aber eigentlich Logisch.

Beides sind Text Dateien. Beide sollten ähnlich lesbar sein.

Chris

Sir Rufo 24. Mär 2014 14:53

AW: INI File Schreiben und auslesen
 
Du solltest verstehen:
  • Was ist eine Datei?
  • Was ist eine Textdatei?
  • Was ist eine INI-Datei?
Denn die Abhängigkeit ist auch wie folgt:
Code:
Datei->Textdatei->INI-Datei
Mit jedem Schritt weiter in die Tiefe wird es immer konkreter und damit spezieller.
Code:
<Datei>
Pfad
Name
Erweiterung
Größe (in Bytes)
Code:
<Textdatei>(<Datei>)
Kodierung (ASCII, ANSI, UTF8, ...)
Zeilenende (CR, LF, CRLF, ...)
ZeilenAnzahl
ZeichenAnzahl


Alle Zeitangaben in WEZ +1. Es ist jetzt 13:05 Uhr.

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz