Inno Setup: Dynamically add a component for all fi

2019-01-24 22:18发布

问题:

Inno Setup: Dynamically add all files in a folder and add a component tag, so during running of the setup the user can select custom setup and select which files to copy.

I'd like to create a Inno Setup file that will grab files in a folder that users can put in there, without having to modify the Inno Setup file each time a new file is added. At the same time, I need the user of the setup file to be able to select which files to copy.

If I do something like this:

Source: "D:\SomeDirectory\*"; DestDir: "{app}"; \
   Flags: ignoreversion recursesubdirs createallsubdirs; Components: dlls

The custom setup only shows the option to copy or not to copy the entire folder.

回答1:

Assuming the files are available on compile time, you can use Inno Setup Preprocessor recursive macro to generate the [Files] and [Components] sections.

This code is partially based on Generating Inno Setup file flags programmatically.

#pragma parseroption -p-

#define FileEntry(Source, Dest) \
    Local[0] = Copy(Dest, 2, Len(Dest) - 1), \
    Local[1] = StringChange(Local[0], ".", ""), \
    "[Files]\n" + \
    "Source: " + Source + "; DestDir: {app}" + ExtractFileDir(Dest) + \
        "; Components: " + Local[1] + "\n" + \
    "[Components]\n" + \
    "Name: " + Local[1] + "; Description: " + ExtractFileName(Dest) + "\n"

#define DirEntry(Source, Dest) \
    Local[0] = Copy(Dest, 2, Len(Dest) - 1), \
    Local[1] = StringChange(Local[0], ".", ""), \
    "[Components]\n" + \
    "Name: " + Local[1] + "; Description: " + ExtractFileName(Dest) + "\n"

#define ProcessFile(Source, Dest, FindResult, FindHandle) \
    FindResult \
        ? \
            Local[0] = FindGetFileName(FindHandle), \
            Local[1] = Source + "\\" + Local[0], \
            Local[2] = Dest + "\\" + Local[0], \
            (Local[0] != "." && Local[0] != ".." \
                ? (DirExists(Local[1]) \
                     ? DirEntry(Local[1], Local[2]) + ProcessFolder(Local[1], Local[2]) \
                     : FileEntry(Local[1], Local[2])) \
                : "") + \
            ProcessFile(Source, Dest, FindNext(FindHandle), FindHandle) \
        : \
            ""

#define ProcessFolder(Source, Dest) \
    Local[0] = FindFirst(Source + "\\*", faAnyFile), \
    ProcessFile(Source, Dest, Local[0], Local[0])

#pragma parseroption -p+

#emit ProcessFolder("D:\SomeDirectory", "")

If D:\SomeDirectory contains these files:

file1.txt
file2.txt
sub1\file11.txt
sub1\file12.txt
sub2\file21.txt
sub2\file22.txt

The above code will generate:

[Files]
Source: D:\SomeDirectory\file1.txt; DestDir: {app}; Components: file1txt
[Components]
Name: file1txt; Description: file1.txt
[Files]
Source: D:\SomeDirectory\file2.txt; DestDir: {app}; Components: file2txt
[Components]
Name: file2txt; Description: file2.txt
[Components]
Name: sub1; Description: sub1
[Files]
Source: D:\SomeDirectory\sub1\file11.txt; DestDir: {app}\sub1; Components: sub1\file11txt
[Components]
Name: sub1\file11txt; Description: file11.txt
[Files]
Source: D:\SomeDirectory\sub1\file12.txt; DestDir: {app}\sub1; Components: sub1\file12txt
[Components]
Name: sub1\file12txt; Description: file12.txt
[Components]
Name: sub2; Description: sub2
[Files]
Source: D:\SomeDirectory\sub2\file21.txt; DestDir: {app}\sub2; Components: sub2\file21txt
[Components]
Name: sub2\file21txt; Description: file21.txt
[Files]
Source: D:\SomeDirectory\sub2\file22.txt; DestDir: {app}\sub2; Components: sub2\file22txt
[Components]
Name: sub2\file22txt; Description: file22.txt

In installer, you will get:


Though note that number of files you can process this way is limited by preprocessor stack.

If you get hit by that, the other (though ugly and complex) way is to use user defined procedures. For an example of implementing a recursive files processing both using approach shown here and using user defined procedures, see Inno Setup - Recurse sub directories without creating those same sub directories.