Jump to content
damos

ini file not writtable

Recommended Posts

I am trying to write on ini file with Delphi Alexandria 11.3 on MacBook Pro ,everything is working except I can't write to ini, the read option is working fine

my ini file

[Server]
Server=192.168.1.103
Database=postgres
Schema=lis
Port=5432
IdleTime=90
Username=nikos
Baseurl=
DicomID=1
Upload=PgTools
Connection=

server:= Settings.ReadString('Server','Server','localhost');
Schema:= Settings.ReadString('Server','Schema','lis');
port:= Settings.ReadString('Server','port','5432');
database:=Settings.ReadString('Server','database','postgres');

all variables are reading Server section
but when i try
Settings.WriteString('Server','Username',Common.CurrentUser);
Settings.WriteString('Server','Connection',DateTimetostr(NOW));
Nothing is happen

i have give permissions read/write to the Pgtools,ini file

For example, this code is working TFile.WriteAllText(TPath.GetHomePath() + TPath.DirectorySeparatorChar + 'Sample.txt', 'Hi over there for current user ');

Any Idea

Share this post


Link to post

How is Settings declared and how instantiated?

Some code would give us a lot more information to help you.

Share this post


Link to post

Hi David

This is simple FMX Splash form (login form) before user start working with connected database (Postgres using Devart PgDac Components for Connection)

All the init connections strings are stored in Pgtools.ini file on directory programs is running , but when deploy on MacOS i change the path as you see my project

here is my code attached files , running on windows write settings is working.

On MacBook Pro the software is running OK , logging is successfully after reading Pgtools.ini file settings (Cannot write)

 

1198347653_Screenshot2023-05-13at1_04_31PM.thumb.png.a3fe85571f8a3505fcfcf4b7d2b137b4.png242797498_Screenshot2023-05-13at1_04.07PM_new.thumb.png.cb116fbc220d10ce99702a9419dad333.png

Splash.fmx

Splash.pas

Share this post


Link to post
17 minutes ago, Hans J. Ellingsgaard said:

Have you checked if the user has write rights on the folder where the inifile is placed?

How to check is on Delphi Delphi deployment or on MacBook Pro

Why this is workig TFile.WriteAllText(TPath.GetHomePath() + TPath.DirectorySeparatorChar + 'Sample.txt', 'Hi over there for current user ');

on the same directory i see new file 'Sample.txt'

Edited by damos

Share this post


Link to post
3 minutes ago, David Heffernan said:

Use a debugger, and look at what is in apath

 

Knowing how to use a debugger is a bit skill. 

Thank you David the problem was not the path i free the  Settings: TIniFile before a write to the sections

 

  if Settings<> nil then Settings.Free;

   Settings:= TIniFile.Create(apath);
   try
   Settings.WriteString('Server','Username',Common.CurrentUser);
   Settings.WriteString('Server','Connection',DateToISO8601(NOW));
    finally
  Settings.Free;
   end;
 

Now is working as you see

1546356760_Screenshot2023-05-13at3_08_49PM.thumb.png.4ea66114692b3e2a7c355bf97df71f52.png

Share this post


Link to post

It is not be necessary to free Settings before creating it. That change cannot have solved your problem, it's something else that you also changed.

Share this post


Link to post
5 minutes ago, dummzeuch said:

It is not be necessary to free Settings before creating it. That change cannot have solved your problem, it's something else that you also changed.

 here is the code without free

 

   server:= Settings.ReadString('Server','Server','localhost');
   Schema:= Settings.ReadString('Server','Schema','lis');
   port:= Settings.ReadString('Server','port','5432');
   database:=Settings.ReadString('Server','database','postgres');
   conectstr:=format(common.Connectstr,[server,database,Schema]) ;
  with dataform do
  Begin
  LisConnection.Port:=port.ToInteger;
  LisConnection.ConnectString:=conectstr;
  try
    LisConnection.Open;
    Mainform.ServerLb.Text:=format(rCommon.sUserConnection,[usernameedit.Text]);
    Mainform.ServerLb.FontColor:=  TAlphaColorRec.Green;
    Common.CurrentUser:=usernameedit.Text;
  //  if Settings<> nil then Settings.Free;
  //  Settings:= TIniFile.Create(apath);
//   try
   Settings.WriteString('Server','Username',Common.CurrentUser);
   Settings.WriteString('Server','Connection',DateToISO8601(NOW));
  //  finally
 // Settings.Free;
 //  end;
     Except
   On E:EXception do
   Mainform.ServerLb.Text:=E.Message;
  end;
  close;
  End;

and the results from ini file without freeing and with freeing Connection= CurrentDatetime

1761652649_Screenshot2023-05-13at3_43_44PM.thumb.png.fc25812daef7b4fdebcbab962c4aacfc.png1388914235_Screenshot2023-05-13at3_49_01PM.thumb.png.956c903676ad264a0ba82b205c0ccf22.png

Share this post


Link to post

None of this is making much sense but whatever. 

 

More generally I strongly recommend you get out of the habit of using global variables. 

Share this post


Link to post
1 hour ago, damos said:

here is the code without free:

server:= Settings.ReadString('Server','Server','localhost');

   Schema:= Settings.ReadString('Server','Schema','lis');

OK, so where do you create and assign Settings in that code? That must be done before using it.

 

1 hour ago, damos said:

 

 

Share this post


Link to post

@damos

as you know, "Setting" will be used in all app, then a "Singleton" would can be used here...

unit uMySingletonSettings;

interface

uses
  System.SysUtils,
  System.Classes;

type
  // I_Your_Interface_If_Want_Use_Without_Free_Object_At_End = interface
  // ['{....}']
  // ... yor methods / getters/setters etc..
  // end;

  TMySettingsSingleton = class { (TInterfacedObject, I_Your_Interface_If_Want_Use_Without_Free_Object_At_End }
  private
    class { }
      var FInstance: TMySettingsSingleton;
    //
    // another vars/const/methods...
  strict private
    FSettings: TStringList;
    //
    constructor Create;
  public
    destructor Destroy; override;
    //
    class function GetInstance: TMySettingsSingleton;
    //
    property MySettings: TStringList read FSettings;
  end;

implementation

{ TMySettingsSingleton }

constructor TMySettingsSingleton.Create;
begin
  if (FSettings = nil) then
    FSettings := TStringList.Create;
end;

destructor TMySettingsSingleton.Destroy;
begin
  FreeAndNil(FSettings);
  //
  inherited;
end;

class function TMySettingsSingleton.GetInstance: TMySettingsSingleton;
begin
  if (FInstance = nil) then
    FInstance := TMySettingsSingleton.Create;
  //
  result := FInstance;
end;

end.

 

in your main unit or DPR just add this

...

initialization

ReportMemoryLeaksOnShutdown := true;
//
TMySettingsSingleton.GetInstance; // creating your StringList...
TMySettingsSingleton.GetInstance; // creating your StringList...  2ª call, dont will create a new StringList FSettings!

finalization

TMySettingsSingleton.GetInstance.Free;

end.

now, just use it as a StringList default usage: (dont need free all time, now! just before app END)

  • TMySettingsSingleton.GetInstance.MySettings.SaveToFile('hello.ini');
  • TMySettingsSingleton.GetInstance.MySettings.LoadFromFile('hello.ini');
    • TMySettingsSingleton.GetInstance.MySettings.Add('key1=value1'); 
    • TMySettingsSingleton.GetInstance.MySettings[0] := 'hello=world';
    • for var L in TMySettingsSingleton.GetInstance.MySettings do ... Memo1.Lines.Add( L );
Edited by programmerdelphi2k

Share this post


Link to post
1 hour ago, dummzeuch said:

OK, so where do you create and assign Settings in that code? That must be done before using it.

 

 

here is my in Splah Unit

 

unit Splash;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,Common,rCommon,
  FMX.Objects, FMX.Edit, FMX.Controls.Presentation, FMX.StdCtrls,Inifiles,System.IOUtils;

type
  TSplashform = class(TForm)
    SplashImage: TImage;
    Label1: TLabel;
    Label2: TLabel;
    lprogram2: TLabel;
    lprogram: TLabel;
    passwordedit: TEdit;
    usernameedit: TEdit;
    Backrect: TRectangle;
    procedure usernameeditKeyDown(Sender: TObject; var Key: Word;
      var KeyChar: Char; Shift: TShiftState);
    procedure passwordeditKeyDown(Sender: TObject; var Key: Word;
      var KeyChar: Char; Shift: TShiftState);
    procedure FormDestroy(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormShow(Sender: TObject);
  private
    server,Schema,port,database, conectstr,apath:string;
    Settings: TIniFile;

  public
    { Public declarations }
  end;

var
   Splashform: TSplashform;


implementation

{$R *.fmx}  uses mdata,fMain,System.DateUtils;

procedure TSplashform.FormCreate(Sender: TObject);

begin
//PgTools:=Tstringlist.Create;
 apath:= TPath.Combine(TPath.GetDirectoryName(ParamStr(0)), 'Pgtools.ini');
  {$IF Defined(MACOS) }
  apath:= StringReplace( apath,'MacOS','Resources/Startup',[rfReplaceAll, rfIgnoreCase]);
    {$ENDIF}

  Settings:= TIniFile.Create(apath);

end;

procedure TSplashform.FormDestroy(Sender: TObject);
begin
if Settings<> nil then Settings.Free;

end;

procedure TSplashform.FormShow(Sender: TObject);
begin
 lprogram.text:=rCommon.sLogoprogram+' current version '+common.OraCurrentVersion;
 usernameedit.Text:=Settings.ReadString('Server','Username','');

end;

procedure TSplashform.passwordeditKeyDown(Sender: TObject; var Key: Word;
  var KeyChar: Char; Shift: TShiftState);
//  var
//  s1,s2:string;
begin
if key=vkReturn then
 Begin
   server:= Settings.ReadString('Server','Server','localhost');
   Schema:= Settings.ReadString('Server','Schema','lis');
   port:= Settings.ReadString('Server','port','5432');
   database:=Settings.ReadString('Server','database','postgres');
   conectstr:=format(common.Connectstr,[server,database,Schema]) ;
  with dataform do
  Begin
  LisConnection.Port:=port.ToInteger;
  LisConnection.ConnectString:=conectstr;
  try
    LisConnection.Open;
    Mainform.ServerLb.Text:=format(rCommon.sUserConnection,[usernameedit.Text]);
    Mainform.ServerLb.FontColor:=  TAlphaColorRec.Green;
    Common.CurrentUser:=usernameedit.Text;
    if Settings<> nil then Settings.Free;
   Settings:= TIniFile.Create(apath);
  try
   Settings.WriteString('Server','Username',Common.CurrentUser);
   Settings.WriteString('Server','Connection',DateToISO8601(NOW));
    finally
  Settings.Free;
  end;
     Except
   On E:EXception do
   Mainform.ServerLb.Text:=E.Message;
  end;
  close;
  End;
 End;
end;

procedure TSplashform.usernameeditKeyDown(Sender: TObject; var Key: Word;
  var KeyChar: Char; Shift: TShiftState);
begin
 if Key = vkReturn then
  begin
  passwordedit.SetFocus
  end;
end;

end.

Share this post


Link to post

@damos

 

you need protect your "Settings" var, because it can be destroy in another place in your app for example, then trying "use/free it" can raise a exception! see my sample above and change it for your usage (with more methods etc... to ensure better use and protection of data)

Share this post


Link to post
24 minutes ago, damos said:

if Settings<> nil then Settings.Free

Replace thus with Settings.Free which internally performs that nil check 

Share this post


Link to post
22 minutes ago, programmerdelphi2k said:

@damos

 

you need protect your "Settings" var, because it can be destroy in another place in your app for example, then trying "use/free it" can raise a exception! see my sample above and change it for your usage (with more methods etc... to ensure better use and protection of data)

Not with the code most recently by the asker. 

 

That singleton code is enormously complex, obfuscates, and is certainly not helpful here. 

Share this post


Link to post
Just now, David Heffernan said:

Not with the code most recently by the asker. 

 

That singleton code is enormously complex, obfuscates, and is certainly not helpful here. 

Ok Thank you

Share this post


Link to post

@damos: Just create the .ini in the homepath, not inside the mac app bundle. I believe there is no write access, only read access.

Share this post


Link to post
3 minutes ago, softtouch said:

@damos: Just create the .ini in the homepath, not inside the mac app bundle. I believe there is no write access, only read access.

Hi thank for answer the only way  working is read and write options are two different approaches , each time you have to free your TInifle var when you switch between read and write when using MacOS platform

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×