DmitryKz  В библиотеке EhLib есть TPropStorageEh. 
 В библиотеке DevExpress есть TPropertiesStore. 
 Ну и я когдато на заре писал? удобно, чем, что: 
 - легко расширяется 
 - не требует подробных указаний, что именно сохранять.  
     Код:   unit VIniValue;   
 interface   
 uses 
   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;   
 type 
   TIniOption = (ioFormCoordinate, ioGridWidth, ioEditValue); 
   TSetIniOptions = set of TIniOption;   
   TVIniValue = class(TComponent) 
   private 
     FormOnCreate : TNotifyEvent; 
     FormOnDestroy: TNotifyEvent; 
     FIniFileName: String; 
     FAutomatic: Boolean; 
     FIniOptions: TSetIniOptions; 
     procedure NewFormOnCreate(Sender : TObject); 
     procedure NewFormOnDestroy(Sender : TObject); 
     procedure SetIniFileName(const Value: String); 
   protected 
     procedure Loaded; override; 
   public 
     constructor Create(AOwner: TComponent); override; 
     procedure Load; 
     procedure Save; 
   published 
     property Automatic: Boolean read FAutomatic write FAutomatic default True; 
     property IniFileName: String read FIniFileName write SetIniFileName; 
     property IniOptions: TSetIniOptions read FIniOptions write FIniOptions; 
   end;     
 procedure Register;   
 implementation   
 uses 
   TypInfo, IniFiles, VVCLUtl, VStrUtl;   
 procedure Register; 
 begin 
   RegisterComponents('Victory Common', [TVIniValue]); 
 end;   
 { TVIniValue }   
 constructor TVIniValue.Create(AOwner: TComponent); 
 begin 
   inherited Create(AOwner); 
   FAutomatic := True; 
 end;   
 procedure TVIniValue.Loaded; 
   function EqualAddr(Addr1, Addr2: TNotifyEvent): Boolean; 
   begin 
     Result := CompareMem(@TMethod(Addr1), @TMethod(Addr2), 8); 
   end; 
 begin 
   inherited Loaded; 
   if not (csDesigning in ComponentState) then begin 
     if FIniFileName = '' then FIniFileName := Name; 
     FIniFileName := ExtractFilePath(ParamStr(0)) + FIniFileName; 
     if FAutomatic and (Owner is TForm) and not EqualAddr((Owner as TForm).OnCreate, NewFormOnCreate) then begin 
       FormOnCreate := (Owner as TForm).OnCreate; 
       (Owner as TForm).OnCreate := NewFormOnCreate; 
       FormOnDestroy := (Owner as TForm).OnDestroy; 
       (Owner as TForm).OnDestroy := NewFormOnDestroy; 
     end; 
   end; 
 end;   
 procedure TVIniValue.NewFormOnCreate(Sender: TObject); 
 begin 
   if Assigned(FormOnCreate) then FormOnCreate(Self); 
   if FAutomatic then Load; 
 end;   
 procedure TVIniValue.NewFormOnDestroy(Sender: TObject); 
 begin 
   if FAutomatic then Save; 
   if Assigned(FormOnDestroy) then FormOnDestroy(Self); 
 end;   
 procedure TVIniValue.Load; 
 var 
   IniFile: TIniFile; 
   Prop : PPropInfo; 
   i,j,V: Integer; 
   s: String; 
 begin 
   IniFile := TIniFile.Create(IniFileName); 
   try 
     if ioFormCoordinate in IniOptions then begin 
       V := IniFile.ReadInteger(Owner.Name, 'FormTop', -1); 
       if V <> -1 then TForm(Owner).Top := V; 
       V := IniFile.ReadInteger(Owner.Name, 'FormLeft', -1); 
       if V <> -1 then TForm(Owner).Left := V; 
       V := IniFile.ReadInteger(Owner.Name, 'FormHeight', -1); 
       if V <> -1 then TForm(Owner).Height := V; 
       V := IniFile.ReadInteger(Owner.Name, 'FormWidth', -1); 
       if V <> -1 then TForm(Owner).Width := V; 
       V := IniFile.ReadInteger(Owner.Name, 'WindowState', -1); 
       if (TForm(Owner).FormStyle <> fsMDIChild) and (V <> -1) and TWinControl(Owner).Showing then TForm(Owner).WindowState := TWindowState(V); 
     end; 
     for i := 0 to Owner.ComponentCount -1 do begin 
       if ioEditValue in IniOptions then begin 
         Prop := GetPropInfo(Owner.Components[i].ClassInfo, 'Text'); 
         if (Prop <> nil) and (Prop^.PropType^.Kind in [tkString,tkLString,tkWString]) then begin 
           s := GetStrProp(Owner.Components[i], Prop); 
           SetStrProp(Owner.Components[i], Prop, IniFile.ReadString(Owner.Name,Owner.Components[i].Name,s)); 
         end; 
         Prop := GetPropInfo(Owner.Components[i].ClassInfo, 'Value'); 
         if (Prop <> nil) and (Prop^.PropType^.Kind in [tkString,tkLString,tkWString]) then begin 
           s := GetStrProp(Owner.Components[i], Prop); 
           SetStrProp(Owner.Components[i], Prop, IniFile.ReadString(Owner.Name,Owner.Components[i].Name,s)); 
         end; 
         if (Prop <> nil) and (Prop^.PropType^.Kind = tkInteger) then begin 
           j := GetOrdProp(Owner.Components[i], Prop); 
           SetOrdProp(Owner.Components[i], Prop, IniFile.ReadInteger(Owner.Name,Owner.Components[i].Name,j)); 
         end; 
         Prop := GetPropInfo(Owner.Components[i].ClassInfo, 'Checked'); 
         if (Prop <> nil) then begin 
           j := GetOrdProp(Owner.Components[i], Prop); 
           SetOrdProp(Owner.Components[i], Prop, Ord(IniFile.ReadBool(Owner.Name,Owner.Components[i].Name,Boolean(j)))); 
         end; 
       end; 
     end; 
   finally 
     IniFile.Free; 
   end; 
 end;   
 procedure TVIniValue.Save; 
 var 
   IniFile: TIniFile; 
   Prop : PPropInfo; 
   i: Integer; 
   WS: TWindowState; 
 begin 
   IniFile := TIniFile.Create(IniFileName); 
   try 
     if ioFormCoordinate in IniOptions then begin 
       WS := TForm(Owner).WindowState; 
       if TForm(Owner).WindowState <> wsNormal then TForm(Owner).WindowState := wsNormal; 
       IniFile.WriteInteger(Owner.Name, 'FormTop', TForm(Owner).Top); 
       IniFile.WriteInteger(Owner.Name, 'FormLeft', TForm(Owner).Left); 
       IniFile.WriteInteger(Owner.Name, 'FormHeight', TForm(Owner).Height); 
       IniFile.WriteInteger(Owner.Name, 'FormWidth', TForm(Owner).Width); 
       IniFile.WriteInteger(Owner.Name, 'WindowState', Integer(WS)); 
       TForm(Owner).WindowState := WS; 
     end; 
     for i := 0 to Owner.ComponentCount -1 do begin 
       if ioEditValue in IniOptions then begin 
         Prop := GetPropInfo(Owner.Components[i].ClassInfo, 'Text'); 
         if (Prop <> nil) and (Prop^.PropType^.Kind in [tkString,tkLString,tkWString]) then 
           IniFile.WriteString(Owner.Name,Owner.Components[i].Name,GetStrProp(Owner.Components[i], Prop));   
         Prop := GetPropInfo(Owner.Components[i].ClassInfo, 'Value'); 
         if (Prop <> nil) and (Prop^.PropType^.Kind in [tkString,tkLString,tkWString]) then 
           IniFile.WriteString(Owner.Name,Owner.Components[i].Name,GetStrProp(Owner.Components[i], Prop)); 
         if (Prop <> nil) and (Prop^.PropType^.Kind = tkInteger) then 
           IniFile.WriteInteger(Owner.Name,Owner.Components[i].Name,GetOrdProp(Owner.Components[i], Prop));   
         Prop := GetPropInfo(Owner.Components[i].ClassInfo, 'Checked'); 
         if (Prop <> nil) then 
           IniFile.WriteBool(Owner.Name,Owner.Components[i].Name,Boolean(GetOrdProp(Owner.Components[i], Prop))); 
       end; 
     end; 
   finally 
     IniFile.Free; 
   end; 
 end;   
 procedure TVIniValue.SetIniFileName(const Value: String); 
 var 
   S: String; 
 begin 
   FIniFileName := Value; 
   S := Copy(FIniFileName, Length(FIniFileName)-4, 4); 
   S := AnsiLowerCase(S); 
   if S <> '.ini' then FIniFileName := FIniFileName+'.ini'; 
 end;   
 end.