Solved

Is it possible to reference variables/objects using variables?

Posted on 2011-09-15
9
283 Views
Last Modified: 2012-06-27
What I'm trying to do is make a program read input such as an INI file or Registry to obtain a list of Variable names plus data, and then process the applications Variables to set the data which was read.

Is this possible?

To help understand what I'd like to achieve, I'm adding two snippets of code.  The first snippet, is an example INI file, and the second snippet is how I envisige reading the INI to get the variable names and variable values, and then to set them.  The text "VariableSlashObject(S) := T;" is the line which I need help with.

Thanks in Advance.
[Labels]
UsernameLabel.Caption=Username
PasswordLabel.Caption=Password

[Edits]
UsernameEdit.Text=Replace this text with your username
PasswordEdit.Text=Star Star Star Star Star
PasswordEdit.PasswordChar=*

Open in new window

procedure TForm1.Button1Click(Sender: TObject);
Var
  S,T : String;
  Sections,Values : TStringList;
  Ini : TIniFile;
  i,j : Integer;
begin
Sections := TStringList.Create;
Values := TStringList.Create;
Ini := TIniFile.Create('c:\Variables.ini');
Ini.ReadSections(Sections);
for i := 0 to Pred(Sections.Count) do
  begin
    Ini.ReadSectionValues(Sections.Strings[i],Values);
    for j := 0 to Pred(Values.Count) do
      begin
        S := Copy(Values.Strings[j],1,Pos('=',Values.Strings[j])-1);
        T := Copy(Values.Strings[j],Pos('=',Values.Strings[j])+1,Length(Values.Strings[j]));
        VariableSlashObject(S) := T;
      end;
  end;
Ini.Free;
Values.Free;
Sections.Free;
end;

Open in new window

0
Comment
Question by:Lester_Clayton
  • 4
  • 3
9 Comments
 
LVL 25

Expert Comment

by:epasquier
ID: 36545603
forget whatever you though you want, you totally have to do it the other way :

your code says which components must be read/wrote in Ini to a procedure that can process an array of components, and that can decide what are the meaningful properties that must be read/wrote for each type of components. In the end, you produce Ini files that are exactly like your sample, but the code is very much more efficient and easy to use.

You're lucky, I have exactly that in store (not the same syntax as yours, not always the same properties saved, but you'll get the catch and can adapt to suit your needs)

usage :
IniSaveComponents( [ UserNameLabel, PasswordLabel, UsernameEdit, PasswordEdit ] , 'Section_Prefix_(Form usually)', True, True );
The last 2 parameters are a bit complex to understand, myself I would do otherwise now, it is to precise which set of properties should be saved : UI level (language captions/colors/font) or Function Level (working parameters, like text in edit or checkbox checked status)
IniOptions.pas
0
 
LVL 36

Expert Comment

by:Geert Gruwez
ID: 36547571
actually i believe you are trying to create a application designer ?
something like the form designer in delphi ?

> look for code generators or form generators
http://www.torry.net/pages.php?id=476

CodeTypo looks interesting
0
 
LVL 25

Expert Comment

by:epasquier
ID: 36548146
no, this does not look like application designer, it looks 100% like my applications Ini files :
for configuration & internationalization, you only need to load/save a very restricted set of properties for each kind of componnent (and always the same).

An application designer will be based on the same technique as DFM files to load components from stream. What wanted Lester is an Ini file equivalent of Loading component from stream. In both case, the solution is based on the file, that provides the names of components and properties, then tries to find the component back in the project (can be in different form) and read/write the property with a chain of ifs ( if p='text' then else if p='value' then ... ) which is all beyond insane to code, and provides very poor performance. Besides, with this, some people could add necessary data in the files to change some properties not wished by the application designer/coder.

Using the Ini file technique the other way is this : The application provides the list of components that need to be loaded/saved (and that is about the only code you need to add to your application). The IniOptions system will then for each such components find the correct method to be used (using IS operator) and each such method specific to a component type knows which properties need to be managed, and how to read/write them in the Ini File.

Because you provide the component list and you don't have to look for each component then property based on text, that method is much more efficient, can let you have more flexibility about the way you name things in the Ini file, and can manage default values if the value is not set in the Ini file when loaded : the current property of the component will be used, untouched. So your initial designed properties will be the Default values for the INI file when it is first regenerated by the application.

While I'm at it, I haven't checked yesterday before posting as I had no time, it is more than probable that you cannot compile it as it is, because it uses some few specific units to manage my own components. Not too many, and the fix is easy :

remove any units in the uses you don't have, remove any procedure using types you don't know about, and remove all the calls to those procedures in IniLoad/SaveComponents
0
 
LVL 9

Author Comment

by:Lester_Clayton
ID: 36549684
Actually what I want to do is find a simple way to set various properties of objects at run time.  25 lines of code is much more preferable to 25 KB of code.  I already know how to read INI files, and set variables based on input, but I'm trying to make the whole process simpler.  I could do this:

UsernameLabel.Caption := Ini.ReadString('Labels','UsernameLabel','Put your username here');
PasswordLabel.Caption := Ini.ReadString('Labels','PasswordLabel','Put your password here');
UsernameEdit.Text := Ini.ReadString('Captions','UsernameEdit','');
PasswordEdit.Text := Ini.ReadString('Captions','PasswordEdit','');
PasswordEdit.Passchar := Ini.ReadString('Captions','PasswordEditPasschar','*');

Open in new window


or I can use the wrappers you provided, but each time I want to set the value inside an object using the INI file, I have to do it in both the INI file and in the main code.  Can you see the value of being able to do it the way I'd like?  I'd just have to add it in the INI file and be finished with it - no extra code, and no need to recompile.

Is what I'm suggesting in my first post possible?
0
What Is Threat Intelligence?

Threat intelligence is often discussed, but rarely understood. Starting with a precise definition, along with clear business goals, is essential.

 
LVL 25

Expert Comment

by:epasquier
ID: 36549847
You only have to set the default properties at design time
The INI file can be regenerated if deleted, no need to change anything here except if you want to test different parameters than default

> Is what I'm suggesting in my first post possible?
I believe I have answered that already. No would be the short answer. Yes might some other people say, but I bet that these solutions would be like coal plants compared to the fusion plant I just gave you.

You could use it easily by adding only one call to IniLoadComponents in your FormCreate event and IniSaveComponents in FormDestroy. Can't be much simpler than that.
Try to understand its principle, and maybe simplify it to suit your needs, and the syntax of the INI file you want.

Or you could just add a few tricks used by IniOptions to your simple coding there, by using as default values of Ini.ReadString the current value of the property (set at design time).
UsernameLabel.Caption := Ini.ReadString('Labels','UsernameLabel',UsernameLabel.Caption);
PasswordLabel.Caption := Ini.ReadString('Labels','PasswordLabel',PasswordLabel.Caption);
UsernameEdit.Text := Ini.ReadString('Captions','UsernameEdit',UsernameEdit.Text);
PasswordEdit.Text := Ini.ReadString('Captions','PasswordEdit',PasswordEdit.Text);

// this would not work, you have to get properly the first char of this string or set #0 if string is empty
PasswordEdit.Passchar := Ini.ReadString('Captions','PasswordEditPasschar',PasswordEdit.Passchar);

Open in new window

0
 
LVL 9

Accepted Solution

by:
Lester_Clayton earned 0 total points
ID: 36578614
Well, I didn't take "no" for an answer, and I've finally got a working piece of code which does exactly what I want it to do.

Here's the entire application

unit RTTIExampleUnit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, System.IniFiles, System.Rtti;

type
  TForm1 = class(TForm)
    Button1: TButton;
    UsernameLabel: TLabel;
    PasswordLabel: TLabel;
    UsernameEdit: TEdit;
    PasswordEdit: TEdit;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure SetObjValueEx(const ObjPath:string; AInstance:TComponent; AValue:TValue);
Var
 i            : integer;
 c            : TRttiContext;
 TargetComp   : string;
 TargetProp   : string;
 p            : TRttiProperty;
begin
  TargetComp:=Copy(ObjPath,1,Pos('.',ObjPath)-1);
  TargetProp:=Copy(ObjPath,Pos('.',ObjPath)+1);
   for i:=0 to AInstance.ComponentCount-1 do
   if CompareText(TargetComp,AInstance.Components[i].Name)=0 then
   begin
       c := TRttiContext.Create;
       try
         p := c.GetType(AInstance.Components[i].ClassInfo).GetProperty(TargetProp);
         if p<>nil then
          p.SetValue(AInstance.Components[i],AValue);
       finally
          c.Free;
       end;
      break;
   end;
end;


procedure TForm1.Button1Click(Sender: TObject);
Var
  S,T : String;
  Sections,Values : TStringList;
  Ini : TIniFile;
  i,j : Integer;
begin
  Sections := TStringList.Create;
  Values := TStringList.Create;
  Ini := TIniFile.Create('c:\Variables.ini');
  Ini.ReadSections(Sections);
  for i := 0 to Pred(Sections.Count) do
    begin
      Ini.ReadSectionValues(Sections.Strings[i],Values);
      for j := 0 to Pred(Values.Count) do
        begin
          S := Copy(Values.Strings[j],1,Pos('=',Values.Strings[j])-1);
          T := Copy(Values.Strings[j],Pos('=',Values.Strings[j])+1,Length(Values.Strings[j]));
          SetObjValueEx(S,self,T);
        end;
    end;
  Ini.Free;
  Values.Free;
  Sections.Free;
end;

end.

Open in new window


Here is the ini file.

[Labels]
UsernameLabel.Caption=Username
PasswordLabel.Caption=Password

[Edits]
UsernameEdit.Text=Replace this text with your username
PasswordEdit.Text=Star Star Star Star Star
PasswordEdit.PasswordChar=*

Open in new window


And here is the result:

RTTI Example
With many thanks to Rodrigo Ruz for his assistance.  He's the author of the The Road to Delphi blog
0
 
LVL 9

Author Comment

by:Lester_Clayton
ID: 36578741
I've requested that this question be closed as follows:

Accepted answer: 0 points for Lester_Clayton's comment http:/Q_27310746.html#36578614

for the following reason:

Anything is possible, as long as we know how to do it :)
0
 
LVL 25

Expert Comment

by:epasquier
ID: 36578742
Well, I wonder why I lost time explaining that this way was wrong on at least 2 counts :
- a) it is inefficient : the lookup of components & properties is heavy
- b) it is unsecured : someone can poke holes in your application, setting any kind of unwanted parameters to any component, just by saying so in the .INI
- c) the .INI allowed content cannot be regenerated with the default values : a must have when anyone but you use your software

and Why have I given to you one of my favorite units ? You haven't given a look at it I guess, or this post would have been closed a week earlier : Only ONE line of code is necessary to implement your needs, maybe not exactly how you though it was best, but then...

I'm objecting to give you a chance to acknowledge some of my advice
0

Featured Post

IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

Suggested Solutions

Title # Comments Views Activity
APK file name 7 63
Activex loadlibrary and show parented form issue 6 263
find a node in VST 2 46
tidtcpserver connection lost handle 2 42
A lot of questions regard threads in Delphi.   One of the more specific questions is how to show progress of the thread.   Updating a progressbar from inside a thread is a mistake. A solution to this would be to send a synchronized message to the…
Introduction I have seen many questions in this Delphi topic area where queries in threads are needed or suggested. I know bumped into a similar need. This article will address some of the concepts when dealing with a multithreaded delphi database…
Access reports are powerful and flexible. Learn how to create a query and then a grouped report using the wizard. Modify the report design after the wizard is done to make it look better. There will be another video to explain how to put the final p…
Here's a very brief overview of the methods PRTG Network Monitor (https://www.paessler.com/prtg) offers for monitoring bandwidth, to help you decide which methods you´d like to investigate in more detail.  The methods are covered in more detail in o…

708 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

17 Experts available now in Live!

Get 1:1 Help Now