damos 0 Posted May 13, 2023 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
David Heffernan 2345 Posted May 13, 2023 We don't know what Settings is. How about a minimal but complete example. Share this post Link to post
dummzeuch 1505 Posted May 13, 2023 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
damos 0 Posted May 13, 2023 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) Splash.fmx Splash.pas Share this post Link to post
Hans J. Ellingsgaard 21 Posted May 13, 2023 Have you checked if the user has write rights on the folder where the inifile is placed? Share this post Link to post
damos 0 Posted May 13, 2023 (edited) 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 May 13, 2023 by damos Share this post Link to post
David Heffernan 2345 Posted May 13, 2023 Use a debugger, and look at what is in apath. Knowing how to use a debugger is a bit skill. Share this post Link to post
damos 0 Posted May 13, 2023 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 Share this post Link to post
dummzeuch 1505 Posted May 13, 2023 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
damos 0 Posted May 13, 2023 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 Share this post Link to post
David Heffernan 2345 Posted May 13, 2023 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
dummzeuch 1505 Posted May 13, 2023 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
programmerdelphi2k 237 Posted May 13, 2023 (edited) @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 May 13, 2023 by programmerdelphi2k Share this post Link to post
damos 0 Posted May 13, 2023 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
programmerdelphi2k 237 Posted May 13, 2023 @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
David Heffernan 2345 Posted May 13, 2023 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
David Heffernan 2345 Posted May 13, 2023 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
damos 0 Posted May 13, 2023 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
programmerdelphi2k 237 Posted May 13, 2023 complexity is a scope of computation, then I think that yes, you can use it, for sure! Share this post Link to post
softtouch 9 Posted May 15, 2023 @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
damos 0 Posted May 15, 2023 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