2

Why can't I get one of my component's index by using this line:

WizardForm.ComponentsList.FindComponent('core').ComponentIndex

If I'm wrong, can anyone point out for me a way to get that index of component?

Matthieu
  • 2,736
  • 4
  • 57
  • 87
BeGiN
  • 363
  • 3
  • 14
  • [`You cannot access component names in components container`](http://stackoverflow.com/questions/22720241/store-the-component-list-and-make-it-default-for-future-installations#comment34625716_22720241). The component name is internally stored in the [`TSetupComponentEntry`](https://github.com/jrsoftware/issrc/blob/is-5_5_4/Projects/Struct.pas#L175) which is not accessible from outside, so, you're out of luck. – TLama Mar 30 '14 at 20:07
  • so there's simply no way to get my components index easier ? :D – BeGiN Mar 30 '14 at 20:10
  • if i have something like Description: "{cm:coremod}"; how can i acces it? – BeGiN Mar 30 '14 at 20:11
  • `WizardForm.ComponentsList.Items.IndexOf(ExpandConstant({cm:coremod})))` gives you index of that item. – TLama Mar 30 '14 at 20:15
  • @TLama Mate , i gotta get you a beer someday :) you are so damn good at this , hehe :D... I wish you only well and thanks again :) i'll answer my own question – BeGiN Mar 30 '14 at 20:25
  • 1
    You're welcome! FYI, the `TSetupComponentEntry` record containing the `Name` member is actually published through `ComponentsList.ItemObject[i]`. The problem is that I haven't ever found a way to access it due to a lack of missing pointer support in scripting language. – TLama Mar 30 '14 at 20:32

2 Answers2

6

Currently, there's no way to find index of a component item in the ComponentsList by the component Name parameter. The Name parameter is stored in the TSetupComponentEntry structure, which is held by the ComponentsList.ItemObject[i] object collection, but you can't access it due to a lack of missing pointer support in Inno Setup Pascal Script.

The only way how to uniquely identify a component in the ComponentsList is by its Description parameter. To find index of a certain component by its description in the ComponentsList, you can use this:

[Components]
Name: "mycomponent"; Description: "Component description"; Types: full

[Code]
...
var
  ItemIndex: Integer;
begin
  ItemIndex := WizardForm.ComponentsList.Items.IndexOf('Component description');
  ...
end;
TLama
  • 75,147
  • 17
  • 214
  • 392
BeGiN
  • 363
  • 3
  • 14
  • @TLama , mate if i have a component description like `en.test=This is a test description` and i know its index , how can i get the `test` from `en.test` custom message? – BeGiN Aug 06 '14 at 13:18
  • Do you mean how to get the caption of an item by the given index ? If so, then you can use `S := WizardForm.ComponentsList.ItemCaption[Index];`. Or, if you're asking how to expand a custom message for the selected language, then you can use `S := CustomMessage('test');`, or expand the `{cm:test}` constant. But if you're asking how to expand a custom message for the language that is not selected, then I'm not sure about the answer and would have to check deeper if it's even possible (and would answer you this in a new question). – TLama Aug 06 '14 at 13:31
  • @Tlama , well i don't need the text , of that description .. i need only the Custom Message name ( e.g: en.comp_name=some description in english, and the Custom Message name is comp_name) this is the procedure that i created: http://pastebin.com/rzzpvkvw this is the output:http://pastebin.com/2Rhr8ixH ... but i need only their CustomMessage name not the entire description :D – BeGiN Aug 06 '14 at 13:39
  • Custom messages are expanded (usually) at startup. After the name of the message is expanded to its value, no one cares which name does it have. So you cannot get a message name from a string value. Inno Setup doesn't provide a list of all custom messages where you could find this information, unless you write some preprocessor script which would build such list, you're out of luck. – TLama Aug 06 '14 at 13:50
  • Could you help me with that ?i have literally no idea how to write a preprocessor script :( i mean i know it's with #ifdefine etc :D but that's all i know – BeGiN Aug 06 '14 at 13:54
  • I did [`something similar`](http://stackoverflow.com/a/11736521/960757) for the `[Files]` section. The principle there was to have an external text file with list of files. The preprocessor opened that file and embedded each line into the `[Files]` section and into the `[Code]` section where was a string list filled by the same lines. You should be able to modify it for your needs (modify the `#emit` function calls). – TLama Aug 06 '14 at 14:01
3

I found a way to do this through WizardSelectedComponents.

Define in var this array of String in order to save components name:

ComponentsName: array of String;

Define this function and this procedure:

Function StringToArray(const Text: String; const Cut: String): array of String;
var
    i: Integer;
    k: Integer;
Begin
    SetArrayLength(Result, 0);
    if Cut = '' then Cut:= #1310;
    Repeat
        k:= Pos(Cut,Text);
        if k = 1 then
        begin
            Delete(Text, 1, Length(Cut));
            CONTINUE
        end;
        SetArrayLength(Result, GetArrayLength(Result) +1); i:= GetArrayLength(Result) -1;
        if k = 0 then
            Result[i]:=Text
        else begin
            Result[i]:= Copy(Text, 1, k -1); Delete(Text, 1, Length(Result[i]) + Length(Cut));
        end;
    Until Length(Text) * k = 0;
End;

procedure InitializeComponentsName();
var
    I: Integer;
    CheckBack: array of boolean;
begin
    SetArrayLength(CheckBack, WizardForm.ComponentsList.ItemCount);

    for I:=0 to WizardForm.ComponentsList.ItemCount-1 do
    begin
        //Saves state in CheckBack.
        CheckBack[I]:=WizardForm.ComponentsList.Checked[I];
        //Only checks non checked components.
        if not CheckBack[I] then WizardForm.ComponentsList.Checked[I]:=true;
    end;

    //Saves components names in ComponentsName array
    ComponentsName:=StringToArray(WizardSelectedComponents(false), ',');

    //Unchecks components that was uncheck previouly.
    //If we try to check a checked component it may crash the Inno program (tested)
    for I:=0 to WizardForm.ComponentsList.ItemCount-1 do
    begin
        if not CheckBack[I] then WizardForm.ComponentsList.Checked[I]:=false;
    end;

    //LOG components name.
    log('COMPONENTS NAME:');
    for I:=0 to GetArrayLength(ComponentsName) -1 do
    begin
        log(ComponentsName[I]);
    end;

end;

You must call this procedure on InitializeWizard. Now we have all components name in ComponentsName array. You can use getComponentName to get your component name:

//Returns component name by Index.
function getComponentName(Index: Integer): String;
begin
    if ((Index>=0) and (Index<GetArrayLength(ComponentsName))) then Result:=ComponentsName[Index];
end;

and GetIndexComponent to get your Index component by name.

//Returns index component by Name. -1 if it doesn't exist.
function GetIndexComponent(ComponentName: String): Integer;
var
    J: Integer;
begin
    Result:= -1;
    for J:=0 to GetArrayLength(ComponentsName)-1 do
    begin
        if (ComponentName=ComponentsName[J]) then
        begin
            Result:=J;
            break;
        end;
    end;
end;

If you add another component to ComponentsList you must call InitializeComponentsName in order to update ComponentsList array.

ELF
  • 41
  • 2
  • It doesn't works with radio buttons yet. I need to modify code. Thanks, TLama, but when there's only way this way is the best. – ELF Mar 27 '15 at 16:55
  • It's sad that Inno doesn't provide a getter method for the component name. Your solution is excellent! Thank you so much. – Jens A. Koch May 06 '15 at 16:30
  • Great Answer, works perfectly. You might need to change `WizardForm.ComponentsList.ItemCount` with `WizardForm.ComponentsList.Items.Count` – idragosalex Oct 03 '18 at 07:27