{------------------------------------------------------------------------------} { Unit Name: IniReadOrder Purpose : To give a read order for registry/ini-files Author : Vesa Lappalainen Date : 3.1.2001 Changed : 6.1.2001/vl + more freedom to create own 'variables' Changed : 7.1.2001/vl + GlobalVariable InsideDelphi added to prevent components with forms created in initialization section to write during design (at the moment the test is done by testing delphi32 from Exename) Changed : 15.9.2001/vl + VCL/CLX compiling (define const CLX) In CLX Registry is replaced also by ini-file ToDo : CheckReadOnly-inifiles write-anyway-specifier If IniReadOrder is in any uses sentence or any file using IniReadOrder is used, then one GlobalIniReadOrder-variable is created. This variable is type of TIniReadOrder with Properties: WriteAll : boolean - if true, then in case of write, write thru every item in read order (default false) CreateLocalIni : boolean - if true, then create local ini-file (to LocalDirectory) even it does not exists. The file is created when first item is writen. If this is false and the file does not exists when starting the program, then the file is not used at all. OnWriteError : TNotifyEvent - handler for WriteErrors StopShowingErrors : boolean - stop showing build in error messages (default false) LastErrorMessage : string - last error message Vars:TkMacroVarList - to read and set and create new macro variables. (see kMacroString) F.ex let var ro : TIniReadOrder; s : string; ... s := ro.Vars['U']; // s := 'uimI' (with default) ro.Vars['%COMPANY%] := 'Nokia'; // change predefined variable ro.Vars['%SUBPROJECT%'] := '3210'; // create and set new variable Value[name:string]:string; default - shortcut to read and set values s := ro['U']; // s := 'uimI' (with default) ro['%COMPANY%] := 'Nokia'; // change predefined variable ro['%SUBPROJECT%'] := '3210'; // create and set new variable Below is listed all predefined variabels with default values: U := 'uimI'; P := 'pimI'; L := 'imI'; M := 'mI'; G := 'I'; u := '%HKCU%;'; p := '%HKCU%\%SUBKEY%;'; i := '%LOCALDIR%\%PROGNAME%.ini|n;'; m := '%HKLM%;'; I := '%PROGDIR%\%PROGNAME%.ini;'; %COMPANY% := 'Kave'; %PROGNAME% := ChangeFileExt(ExtractFileName(ParamStr(0)),''); %SUBKEY% := ''; %HKCU% := 'HKEY_CURRENT_USER\software\%COMPANY%\%PROGNAME%'; %HKLM% := 'HKEY_LOCAL_MACHINE\software\%COMPANY%\%PROGNAME%'; %PROGDIR% := ExtractFilePath(ParamStr(0)); %LOCALDIR% := GetCurrentDir(); About read orders: "Variables" is used to solve the read order for registry/ini-files. For read order the following default abrevations is used: U - global User-order (uimI) - default P - project read order (pimI) L - global local-Ini order (imI) M - global Machine-order (mI) G - global global-Ini order (I) The order can be given also by "direct access":, f.ex Vars['U'] := 'HKCU\software\kave\IniTest';'IniTest.ini' One "order" can contain many "reg-ini parts", separated by ; If sequence has less than 6 characters it means abrevation, if it starts with \HK or HK then it means registry entry, else it means Ini-file. Ini-file with no path is first tested from current directory, if it does not exists then program directory is used. F.ex: Vars['U'] := 'HKCU\software\kave\IniTest|n';I Following access specifiers is used after reg/ini n - if the key does not exist, then do not create it r - key is only for reading. Newer write to that key, instead write to next key in the order Default is Methods: procedure ReadReplace(const ininame: string='kini.ini'); - read new values from ini-files to all parametres (User/Local). If some value is not in ini-file, then value is not changed. procedure RefreshLocalDirectory; - read new value for LocalDirectory using GetCurentDir function ReadString(const order,sec,item:string;const def:string='') : string; - read string using the order-sequence. For example if Company:='Kave'; ProgramName:='IniTest'; the call ReadString('U','Pos','FormIniTest','') tries to read the value in following order (if User='uimI'): reg: \HKEY_CURRENT_USER\software\Kave\IniTest\Pos\FormIniTest ini: LocalDirectory\IniTest.ini sec: [Pos] item: FormIniTest reg: \HKEY_LOCAL_MACHINE\software\Kave\IniTest\Pos\FormIniTest ini: ProgDirectory\IniTest.ini sec: [Pos] item: FormIniTest if not found from any place then default is returned If order=''; then U is used as default. procedure WriteString(const order,sec,item,s:string); virtual; - write string to first item in read sequence. in previous example: WriteString('U','Pos','FormIniTest','12,30,100,200'); is writen in \HKEY_CURRENT_USER\software\Kave\IniTest\Pos\FormIniTest If WriteAll=True then the value is writen to every item in read sequence. Changing defaults outside of the program: The order can be replaced temporarily (f.ex install run) by writing kini.ini file to the program directory: [IniReadOrder] CreateLocalIni=1 => Creates allways local-ini file if in read order WriteAll=1 => to write thru every item, otherwise write only to the first item on the list [IniReadOrderVars] U=I => U := I L=I => L := I M=I => M := I G=I => G := I %COMPANY%=Nokia %SUBPROJECT%=3210 'u=I => u := I // note that lower case one char vars // must be started with ' in kini.ini Note that if vars is changed in main-program, then those must be overriden by calling method ReadReplace Changing defaults for program: To change f.ex the company name in program either put GlobalIniReadOrder.Vars['%COMPANY%'] := 'Nokia'; as the first line in main program. Or make an unit, f.exe Nokia.pas and write to the end of file initialization begin GlobalIniReadOrder.Vars['%COMPANY%'] := 'Nokia'; end; and put the file name to any uses-sentence in the program. Sometimes it is best to put to main file's first uses sentence (if components are created in initialization sections). Same way the read sequence for any item (U, L, M, G) can be changed: GlobalIniReadOrder['U'] := 'uI'; Global functions: function IniOrderReadString(const order,sec,item:string;const def:string='') : string; - read string using order-sequence to read. GlobalIniReadOrder is used to solve order function IniOrderReadInteger(const order,sec,item:string; const def:integer=0) : integer; - read integer using order-sequence to read. GlobalIniReadOrder is used to solve order procedure IniOrderWriteString(const order,sec,item,s:string); - write string using order-sequence to read. GlobalIniReadOrder is used to solve order procedure IniOrderWriteInteger(const order,sec,item:string; s:integer); - write integer using order-sequence to read. GlobalIniReadOrder is used to solve order } {------------------------------------------------------------------------------} unit IniReadOrder; interface uses IniFiles,Classes, {$ifdef CLX} QControls, {$ifdef MSWINDOWS} registry, {$endif} {$else} Controls,windows,registry, {$endif} kMacroString; type TOneReadOrder = class(TStringList) private function GetOrder(i: integer): TCustomIniFile; procedure SetOrder(i: integer; const Value: TCustomIniFile); public destructor Destroy; override; procedure Add(const name:string;f:TCustomIniFile); reintroduce; overload; virtual; property Order[i:integer]:TCustomIniFile read GetOrder write SetOrder; default; end; TIniList = class(TStringList) private function GetIni(i: integer): TCustomIniFile; public destructor Destroy; override; procedure Add(const key:string;inifile:TCustomIniFile); reintroduce; overload; virtual; property Ini[i:integer]:TCustomIniFile read GetIni; default; end; TkLocalIniFile = class(TIniFile) private FReadOnly : boolean; procedure SetReadOnly(const Value: boolean); public constructor Create(const FileName: string; readonly : boolean = false); overload; virtual; destructor Destroy; override; property ReadOnly : boolean read FReadOnly write SetReadOnly; procedure WriteString(const Section, Ident, Value: String); override; end; {$ifdef MSWINDOWS} {$ifdef CLX} HKEY = integer; {$endif} TkRegistryIniFile = class(TRegistryIniFile) private FFileName: string; FKey : HKEY; FOpenForWrite : boolean; FReadOnly: boolean; FOpenOK: boolean; procedure SetFileName(const Value: string); public constructor Create(const FileName: string; key:HKEY=0); virtual; destructor Destroy; override; property FileName : string read FFileName write SetFileName; property ReadOnly : boolean read FReadOnly write FReadOnly; property OpenOK : boolean read FOpenOK; procedure WriteString(const Section, Ident, Value: String); override; end; {$else} HKEY = integer; TkRegistryIniFile = class(TkLocalIniFile) private FFileName: string; // FKey : HKEY; // FOpenForWrite : boolean; FOpenOK: boolean; procedure SetFileName(const Value: string); public constructor Create(const FileName: string; key:HKEY=0); overload; destructor Destroy; override; property FileName : string read FFileName write SetFileName; property OpenOK : boolean read FOpenOK; procedure WriteString(const Section, Ident, Value: String); override; end; {$endif} TIniReadOrder = class private FWriteAll: boolean; FOrderList : TStringList; FIniCacheList : TIniList; FVars : TkMacroVarList; FCreateLocalIni: boolean; FStopShowingErrors : boolean; FOnWriteError: TNotifyEvent; FLastErrorMessage: string; procedure SetCreateLocalIni(const Value: boolean); procedure AddRegOrIniList(list: TOneReadOrder; const s: string); procedure SetLastErrorMessage(const Value: string); procedure AddIniFiles(r: TOneReadOrder; order: string ); function GetValue(const name: string): string; procedure SetValue(const name, Value: string); protected procedure SetWriteAll(const Value: boolean); virtual; procedure ClearIniFiles; virtual; function GetOneOrder(const s: string): TOneReadOrder; virtual; procedure ClearOrderList; virtual; procedure OnVarChange(sender:TkMacroVarList;i:integer); virtual; public constructor Create; virtual; destructor Destroy; override; procedure ReadReplace(const ininame: string='kini.ini'); virtual; procedure RefreshLocalDirectory; virtual; function ReadString(const order,sec,item:string;const def:string='') : string; virtual; procedure WriteString(const order,sec,item,s:string); virtual; property Value[const name:string]:string read GetValue write SetValue; default; published property WriteAll : boolean read FWriteAll write SetWriteAll; property CreateLocalIni : boolean read FCreateLocalIni write SetCreateLocalIni; property OnWriteError : TNotifyEvent read FOnWriteError write FOnWriteError; property StopShowingErrors : boolean read FStopShowingErrors write FStopShowingErrors; property LastErrorMessage : string read FLastErrorMessage write SetLastErrorMessage; property Vars : TkMacroVarList read FVars; end; function IniOrderReadString(const order,sec,item:string;const def:string='') : string; function IniOrderReadInteger(const order,sec,item:string; const def:integer=0) : integer; function IniOrderReadDouble(const order,sec,item:string; const def:double=0.0) : double; function IniOrderReadBoolean(const order,sec,item:string; const def:boolean=false) : boolean; procedure IniOrderWriteString(const order,sec,item,s:string); procedure IniOrderWriteInteger(const order,sec,item:string; s:integer); procedure IniOrderWriteDouble(const order,sec,item:string; s:double; des:integer=-1); procedure IniOrderWriteBoolean(const order,sec,item:string; s:boolean); function CreateTkLocalIniFile(const FileName: string; allow : boolean = false) : TkLocalIniFile; overload; function CreateTkLocalIniFile(const FileName: string; const access:string) : TkLocalIniFile; overload; function CreateTkRegistryIniFile(const FileName: string; const access:string) : TkRegistryIniFile; overload; var GlobalIniReadOrder : TIniReadOrder; InsideDelphi : boolean; implementation uses Sysutils,kdouble, {$ifdef CLX} QDialogs,QForms, {$else} Dialogs,Forms, {$endif} {$ifdef MSWINDOWS} GetRegRoot, {$endif} kstring,IniName; {$ifdef CLX} const mbYesToAll : TMsgDlgBtn = mbOK; {$else} {$endif} {------------------------------------------------------------------------------} function IniOrderReadString(const order,sec,item:string;const def:string='') : string; begin Result := GlobalIniReadOrder.ReadString(order,sec,item,def); end; function IniOrderReadInteger(const order,sec,item:string; const def:integer=0) : integer; begin Result := StrToIntDef(GlobalIniReadOrder.ReadString(order,sec,item,''),def); end; function IniOrderReadDouble(const order,sec,item:string; const def:double=0.0) : double; begin Result := IniStrToDouble(GlobalIniReadOrder.ReadString(order,sec,item,''),def); end; function IniOrderReadBoolean(const order,sec,item:string; const def:boolean=false) : boolean; var s:string; begin if def then s:='1' else s:='0'; s:=GlobalIniReadOrder.ReadString(order,sec,item,s); if (s='1') or (s='true') then result:=true else result:=false; end; {------------------------------------------------------------------------------} procedure IniOrderWriteString(const order,sec,item,s:string); begin GlobalIniReadOrder.WriteString(order,sec,item,s); end; procedure IniOrderWriteInteger(const order,sec,item:string; s:integer); begin GlobalIniReadOrder.WriteString(order,sec,item,IntToStr(s)); end; procedure IniOrderWriteDouble(const order,sec,item:string; s:double; des:integer=-1); begin GlobalIniReadOrder.WriteString(order,sec,item,DoubleToIniStr(s,des)); end; procedure IniOrderWriteBoolean(const order,sec,item:string; s:boolean); var sStr:string; begin if s then sStr:='1' else sStr:='0'; GlobalIniReadOrder.WriteString(order,sec,item,sStr); end; { TIniReadOrder } procedure TIniReadOrder.ReadReplace(const ininame: string); var ini : TIniFile; begin ini := TIniFile.Create(GetIniName(ininame)); FWriteAll := ini.ReadBool('IniReadOrder','WriteAll',FWriteAll); FCreateLocalIni := ini.ReadBool('IniReadOrder','CreateLocalIni',FCreateLocalIni); ini.Free; Vars.ReadMacros(ininame,'IniReadOrderVars'); ClearIniFiles; end; procedure TIniReadOrder.RefreshLocalDirectory; begin Vars['%LOCALDIR%'] := GetCurrentDir(); end; procedure TIniReadOrder.AddRegOrIniList(list:TOneReadOrder; const s:string); var p : integer; inifile : TCustomIniFile; st,access : string; begin if ( list.Find(s,p) ) then exit; if ( FIniCacheList.Find(s,p) ) then begin list.Add(st,FIniCacheList.Ini[p]); exit; end; access := s; st := ExtractString(access,'|'); p := Pos('HK',st); if ( p in [1,2] ) then begin inifile := CreateTkRegistryIniFile(st,access); list.Add(st,inifile); FIniCacheList.Add(st,inifile); exit; end; inifile := CreateTkLocalIniFile(GetIniName(st),access); list.Add(st,inifile); FIniCacheList.Add(st,inifile); end; {$ifdef DELPHI5} const PathDelim='\'; {$endif} procedure TIniReadOrder.AddIniFiles( r:TOneReadOrder;order:string); var part : string; begin order := Vars.Change(order); while ( order <> '' ) do begin part := Trim(ExtractString(order,';')); part := ChangeAll(part,'\',PathDelim); part := ChangeAll(part,'/',PathDelim); part := ChangeAll(part,PathDelim+PathDelim,PathDelim); if ( part <> '' ) then AddRegOrIniList(r,part); end; end; function TIniReadOrder.GetOneOrder(const s:string):TOneReadOrder; var i:integer; order : string; begin Result := nil; if ( s = '-' ) then exit; order := Trim(s); if ( order = '' ) then order := 'U'; if ( FOrderList.Find(order,i) ) then begin Result := TOneReadOrder(FOrderList.Objects[i]); exit; end; Result := TOneReadOrder.Create; FOrderList.AddObject(order,Result); AddIniFiles(Result,order); end; function TIniReadOrder.ReadString(const order, sec, item, def: string): string; var readOrder : TOneReadOrder; i : integer; s : string; begin if ( InsideDelphi ) then exit; Result := def; if ( sec = '' ) or ( item = '' ) then exit; // Result := Ini.ReadString(sec,item,def); readOrder := GetOneOrder(order); if ( readOrder = nil ) then exit; for i:=0 to readOrder.Count-1 do begin if ( readOrder[i] = nil ) then continue; s := readOrder[i].ReadString(sec,item,'§'); if ( s <> '§' ) then begin Result := s; exit; end; end; end; procedure TIniReadOrder.WriteString(const order, sec, item, s: string); var readOrder : TOneReadOrder; i : integer; begin if ( InsideDelphi ) then exit; if ( order = '-' ) then exit; if ( sec = '' ) or ( item = '' ) then exit; readOrder := GetOneOrder(order); if ( readOrder = nil ) then exit; for i:=0 to readOrder.Count-1 do begin try if ( readOrder[i] = nil ) then continue; readOrder[i].WriteString(sec,item,s); if ( not WriteAll ) then break; except on e:Exception do begin if ( not FStopShowingErrors ) then if ( MessageDlg(e.Message +#13#10+'Stop showing errors?', mtError,[mbCancel,mbYesToAll],0) <> mrCancel ) then FStopShowingErrors := true; LastErrorMessage := e.Message; if ( Assigned(OnWriteError) ) then OnWriteError(self); // ShowMessage(e.Message); end; end; end; end; procedure TIniReadOrder.SetWriteAll(const Value: boolean); begin FWriteAll := Value; end; procedure TIniReadOrder.ClearIniFiles; var i:integer; begin ClearOrderList; for i:=0 to FIniCacheList.Count-1 do begin FIniCacheList.Objects[i].Free; end; FIniCacheList.Clear; end; constructor TIniReadOrder.Create; begin inherited; FVars := TkMacroVarList.Create; Vars.ShortChangeLimit := 6; Vars.ShortVarLenLimit := 1; Vars.Separator := ';'; FOrderList := TStringList.Create; FOrderList.Sorted := true; FIniCacheList := TIniList.Create; FIniCacheList.Sorted := true; Vars['U'] := 'uimI'; Vars['P'] := 'pimI'; Vars['L'] := 'imI'; Vars['M'] := 'mI'; Vars['G'] := 'I'; Vars['u'] := '%HKCU%;'; Vars['p'] := '%HKCU%\%SUBKEY%;'; Vars['i'] := '%LOCALDIR%\%PROGNAME%.ini|n;'; Vars['m'] := '%HKLM%;'; Vars['I'] := '%PROGDIR%\%PROGNAME%.ini;'; Vars['%COMPANY%'] := 'Kave'; if Assigned(GlobalIniReadOrder) then Vars['%COMPANY%'] := GlobalIniReadOrder.Vars['%COMPANY%']; Vars['%PROGNAME%'] := ChangeFileExt(ExtractFileName(ParamStr(0)),''); Vars['%SUBKEY%'] := ''; {$ifdef MSWINDOWS} Vars['%HKCU%'] := 'HKEY_CURRENT_USER\software\%COMPANY%\%PROGNAME%'; Vars['%HKLM%'] := 'HKEY_LOCAL_MACHINE\software\%COMPANY%\%PROGNAME%'; {$else} Vars['%HKCU%'] := GetEnvVariable('HOME')+'/software/%COMPANY%/%PROGNAME%.ini'; Vars['%HKLM%'] := '%PROGDIR%/%PROGNAME%.ini'; {$endif} Vars['%PROGDIR%'] := ExtractFilePath(ParamStr(0)); RefreshLocalDirectory; // sisältää CreateIniFiles Vars.OnMacroChange := OnVarChange; ClearIniFIles; end; destructor TIniReadOrder.Destroy; begin ClearOrderList; ClearIniFiles; FIniCacheList.Free; FOrderList.Free; FVars.Free; inherited; end; procedure TIniReadOrder.ClearOrderList; var i:integer; begin for i:=0 to FOrderList.Count-1 do begin FOrderList.Objects[i].Free; end; FOrderList.Clear; end; procedure TIniReadOrder.SetCreateLocalIni(const Value: boolean); begin if ( FCreateLocalIni = Value ) then exit; FCreateLocalIni := Value; ClearIniFiles; end; procedure TIniReadOrder.SetLastErrorMessage(const Value: string); begin FLastErrorMessage := Value; end; procedure TIniReadOrder.OnVarChange(sender: TkMacroVarList; i: integer); begin ClearIniFiles; end; function TIniReadOrder.GetValue(const name: string): string; begin Result := Vars[name]; end; procedure TIniReadOrder.SetValue(const name, Value: string); begin Vars[Name] := Value; end; { TOneReadOrder } procedure TOneReadOrder.Add(const name:string;f: TCustomIniFile); begin // if ( f = nil ) then exit; inherited AddObject(name,f); end; destructor TOneReadOrder.Destroy; begin inherited; end; function TOneReadOrder.GetOrder(i: integer): TCustomIniFile; //var p : ^TIniFile; begin Result := nil; if ( i < 0 ) or ( i >= Count ) then exit; Result := TCustomIniFile(Objects[i]); // p := Items[i]; // Result := p^; end; procedure TOneReadOrder.SetOrder(i: integer; const Value: TCustomIniFile); begin if ( i < 0 ) or ( i >= Count ) then exit; Objects[i] := Value; end; { TkRegistryIniFile } constructor TkRegistryIniFile.Create(const FileName: string; key:HKEY); begin {$ifdef MSWINDOWS} inherited Create(''); FFileName := FileName; FKey := GetRoot(FFileName); if ( key <> 0 ) then FKey := key; RegIniFile.RootKey := FKey; FOpenOK := RegIniFile.OpenKey(FFileName,false); FOpenForWrite := false; {$else} inherited Create(FileName); {$endif} end; destructor TkRegistryIniFile.Destroy; begin inherited; end; procedure TkRegistryIniFile.SetFileName(const Value: string); begin FFileName := Value; end; procedure TkRegistryIniFile.WriteString(const Section, Ident,Value: String); begin {$ifdef MSWINDOWS} if ( ReadOnly ) then exit; if ( not FOpenForWrite ) then begin RegIniFile.CloseKey; RegIniFile.RootKey := FKey; FOpenOK := RegIniFile.OpenKey(FFileName,true); if ( FOpenOK ) then FOpenForWrite := true else begin FOpenOK := RegIniFile.OpenKey(FFileName,false); exit; end end; RegIniFile.WriteString(Section,Ident,Value); {$else} inherited; {$endif} end; { TkLocalIniFile } constructor TkLocalIniFile.Create(const FileName: string; readonly : boolean); begin inherited Create(FileName); FReadOnly := readonly; end; destructor TkLocalIniFile.Destroy; begin UpdateFile; inherited; end; procedure TkLocalIniFile.SetReadOnly(const Value: boolean); begin FReadOnly := Value; end; procedure TkLocalIniFile.WriteString(const Section, Ident, Value: String); begin if ( not ReadOnly ) then inherited; end; function CreateTkLocalIniFile(const FileName: string; allow : boolean = false) : TkLocalIniFile; begin Result := nil; if ( not FileExists(FileName ) ) and ( not allow ) then exit; Result := TkLocalIniFile.Create(FileName,allow); end; function CreateTkLocalIniFile(const FileName: string; const access:string) : TkLocalIniFile; var exists,canwrite,ro : boolean; begin Result := nil; exists := FileExists(FileName ); if ( not exists ) and ( access = 'n' ) then exit; if ( exists ) then canwrite := CanWriteToFile(FileName) else canwrite := CanCreateFile(FileName); ro := ( not canwrite ) and ( exists ); // if ro then exit; Result := TkLocalIniFile.Create(FileName, ( access='r' ) or ( ro ) ); end; function CreateTkRegistryIniFile(const FileName: string; const access:string) : TkRegistryIniFile; begin Result := TkRegistryIniFile.Create(FileName); if ( not Result.OpenOK ) and ( access = 'n' ) then begin Result.Free; Result := nil; end; Result.ReadOnly := access = 'r'; end; { TIniList } procedure TIniList.Add(const key: string; inifile:TCustomIniFile); begin // if ( inifile <> nil ) then AddObject(key,inifile); end; destructor TIniList.Destroy; begin inherited; end; function TIniList.GetIni(i: integer): TCustomIniFile; begin Result := nil; if ( i < 0 ) or ( i >= Count ) then exit; Result := TCustomIniFile(Objects[i]); end; initialization begin GlobalIniReadOrder := TIniReadOrder.Create; with GlobalIniReadOrder do begin ReadReplace(); end; InsideDelphi := Pos('delphi32',LowerCase(Application.ExeName)) > 0; end; finalization begin GlobalIniReadOrder.Free; end; end.