I need a help with condition in [Run]. If it's possible...
I need to run a command that depends on a condition.
Like this:
if (UserPage.Values[0] = 'NC') then FileName: {sys}\inetsrv\appcmd.exe; Parameters: "set......"
Or other way to do it.
Regards.
You are looking for the Check parameter:
[Run]
FileName: "{sys}\inetsrv\appcmd.exe"; Parameters: "set......"; Check: ShouldRun
[Code]
function ShouldRun: Boolean;
begin
Result := (UserPage.Values[0] = 'NC');
end;
Related
My scenario is currently that I'd like the possibility for users to skip installation of certain files that a user might have specified via a command line argument at install time.
The idea would be for the user to specify a text file and ideally during installation, the installer would check if the file currently being copied is listed in the supplied text file and decide depending on that.
My [Files] section atm is referencing full directories: would the best approach be to list all files individually and make use of the Check parameter or is there a different approach available? There seems to be no event function that would lend itself for this task.
Thanks
Markus
No need to list all files individually. The Check function is called for each file individually, even when the Source is a wildcard.
Use the CurrentFilename function to tell what file is being processed at the moment.
Note that the Check function is called multiple times for each file.
[Files]
Source: "C:\path\*.*"; DestDir: "{app}"; Check: AllowFile
[Code]
var
Whitelist: TStringList;
function AllowFile: boolean;
var
FileName: string;
begin
FileName := ExtractFileName(ExpandConstant(CurrentFileName));
Result := (Whitelist = nil) or (Whitelist.IndexOf(Uppercase(FileName)) >= 0);
if Result then
Log(Format('Allowing "%s"', [FileName]))
else
Log(Format('Skipping "%s"', [FileName]));
end;
function InitializeSetup(): Boolean;
var
WhitelistFile: string;
Lines: TArrayOfString;
I: Integer;
begin
WhitelistFile := ExpandConstant('{param:Whitelist}');
if WhitelistFile = '' then
begin
Log('No whitelist specified, allowing all files');
end
else
begin
Whitelist := TStringList.Create;
Whitelist.Sorted := True;
LoadStringsFromFile(WhitelistFile, Lines);
for I := 0 to GetArrayLength(Lines) - 1 do
Whitelist.Add(Uppercase(Lines[I]));
Log(Format('Loaded %d entries to whitelist from "%s"', [
Whitelist.Count, WhitelistFile]));
end;
Result := True;
end;
I wrote a setup script to install a third-party setup.exe that I run to install it silently. When I run this external setup.exe, I must specify what products to install through its command-line parameters. For this, I have a dozen of tasks, each representing the products to be installed or not.
Because are too many tasks, I thought in a simpler and smarter way than writing this line below with And/And Not task checks 12*12 times...
[Run]
Filename: {tmp}\Setup.exe; Parameters: /ProductNames=product1,product2,etc...; \
Flags: shellexec WaitUntilTerminated; StatusMsg: Installing products...; \
Tasks: product1 and product2 and not product3 etc...
Maybe this is not the best approach to achieve this, but in the [Code] section I declared some variables that stores the product names to install or not, and I would like to be able reference those vars, like this:
[Run]
Filename: {tmp}\Setup.exe; Parameters: /ProductNames={%product1}{%product2}; \
Flags: shellexec WaitUntilTerminated; StatusMsg: Installing products...;
So that will simplify the [Run] section to just that line, instead of dozens and dozens of variations...
How can I do this?.
If it is not possible, then as an alternative I can declare a function that returns a comma-delimited string with the product names to be installed, but I don't know how to call it in the [Run] section above to use the return value of that function inside the Parameters string of the external setup.exe that I run... I mean, something like this
[Run]
Filename: {tmp}\Setup.exe; Parameters: /ProductNames={MyFunction()}; \
Flags: shellexec WaitUntilTerminated; StatusMsg: Installing products...;
This is a minified example of my [Code] section:
[Code]
var
product1: string;
product2: string;
procedure TaskOnClickCheck(Sender: TObject);
begin
{ reset values. }
product1 := ''
product2 := ''
if IsTaskSelected('product1') then
begin
product1 := ',product1 Name'
end;
if IsTaskSelected('product2') then
begin
product2 := ',product2 Name'
end;
end;
procedure InitializeWizard();
begin
WizardForm.TasksList.OnClickCheck := #TaskOnClickCheck;
end;
Use a scripted constant:
[Run]
Filename: {tmp}\Setup.exe; Parameters: /ProductNames={code:GetProducts}; \
Flags: shellexec waituntilterminated; StatusMsg: Installing products...
[Tasks]
Name: product1; Description: "Product 1"
Name: product2; Description: "Product 2"
Name: product3; Description: "Product 3"
[Code]
function GetProducts(Param: string): string;
var
ProductList: TStringList;
begin
ProductList := TStringList.Create;
if IsTaskSelected('product1') then ProductList.Add('Prod1');
if IsTaskSelected('product2') then ProductList.Add('Prod2');
if IsTaskSelected('product3') then ProductList.Add('Prod3');
Result := ProductList.CommaText;
ProductList.Free;
Log('Product list is: ' + Result);
end;
ProductList.CommaText above will produce an output like Prod1,Prod2 (though if there are spaces in product names, it will produce "Prod 1","Prod 2").
AfterInstall function is always called, even if file already exists :
[Files]
Source: "myfile.txt"; DestDir: "{app}"; Flags: onlyifdoesntexist; \
AfterInstall: MyAfterInstall('{app}\myfile.txt')
In case "myfile.txt" already exists, it is not overwritten (not installed) and AfterInstall should not be called in this case (in my opinion).
You can use Check parameter instead of onlyifdoesntexist flag.
[Files]
Source: "MyProg.exe"; DestDir: "{app}"; Check: OnlyIfDoesntExist; \
AfterInstall: MyAfterInstall
[Code]
function OnlyIfDoesntExist: Boolean;
var
FileName: string;
begin
FileName := ExpandConstant(CurrentFilename);
Result := not FileExists(FileName);
if Result then
begin
Log(Format('Installing "%s" as the file does not exists yet.', [FileName]));
end
else
begin
Log(Format('Skipping "%s" as the files exists already.', [FileName]));
end;
end;
procedure MyAfterInstall;
begin
Log(Format('Installed "%s".', [ExpandConstant(CurrentFilename)]));
end;
Note that the Check function is called several times, so you will see the respective message several times in the log. If you happen to turn the check to an expensive function, you better cache the results.
Also note above that you do not have to pass a file name to your AfterInstall procedure, as you can retrieve the name using CurrentFilename function.
I used the code from this Answer from the Member TLama. I think it's exactly what i need, but I have two problems with it:
I need the Serial from the edit boxes in the registry. This is what i tried:
Root: "HKCU"; Subkey: "Software\myProg"; ValueType: string; ValueName: "Serial"; ValueData: "{code:GetSerialNumber}"; Flags: deletevalue uninsdeletevalue
but Inno gives me an error. TLama wrote in his answer (from the Link above), it's enought to call the GetSerialNumber part, but I do sth. wrong...
The other question: Is it possible to prefill the serialbox with an example code? E.g. 12345 or abcde? I'm using only one input box with 10 chars...
Hope someone can help, and sorry for my bad english ;)
You can use UserInfoPage and then {userinfoserial} but if you want to use TLama's solution then you should slightly change NextButtonClick function:
function NextButtonClick(CurPageID: Integer): Boolean;
var
S: string;
I: Integer;
begin
Result := True;
if CurPageID = SerialPage.ID then
begin
S := '';
for I := 0 to High(SerialEdits) do
S := S + SerialEdits[I].Text + '-';
SetLength(S, Length(S)-1);
RegWriteStringValue(HKEY_CURRENT_USER, 'Software\myProg',
'Serial', S);
end;
end;
Var SerialEdits: array of TEdit; has to be set as Global for the script. You also may want to add key to registry later (e.g. with CurStepChanged when ssDone or something) or write your own function that will pass Serial to Result as String and then call it in Registry Section.
I want to change defaultdirname parameter in ssInstall part. How can I do that? Is there a function for setting [Setup] parameters.
The following global objects are available :
MainForm of type TMainForm, WizardForm of type TWizardForm and UninstallProgressForm of type TUninstallProgressForm and one special constant: crHand of type TControl.Cursor.
If you want to set the default directory in the wizard, just access to it's componants like you would in normal delphi code.
For example, set the directory to a custom value:
WizardForm.DirEdit.Text := 'c:\test';
to read that value you can use the WizardDirValue function.
I say 'just access'... but it took me an hour to figure out ;)
There seems to be no way to change a script constant via scripting.
I think your best bet is to modify the target directory for each entry in the [Files] section, e.g.
[Files]
Source: "MYPROG.EXE"; DestDir: "{code:NewTargetDir}"
and derive your new installation directory like this:
[Code]
function NewTargetDir(Param: String): String;
begin
Result := ExpandConstant('{app}') + '\MySubDir';
end;
Since the NewTargetDir function will be called just before the file is actually copied, this should work.
However, I think you should reconsider your approach. First asking the user to specify a directory to installinto, and then actually installing into a different directory, which seems to be your intent, is the wrong way, IMO. Do you really have a compelling reason to install into another directory than the one specified by the user? Besides, the result of my example code could just as well be achieved by specifying
[Files]
Source: "MYPROG.EXE"; DestDir: "{app}\MySubDir"
without any scripting needed. When in doubt, go for the simpler solution.
I have a similar situation, where the setup app is receiving the install path from the command line.
I'm using the solution proposed by Jonx:
WizardForm.DirEdit.Text := 'c:\test';
Example:
function CompareParameter(param, expected: String): Boolean;
begin
Result := False;
if Length(param) >= Length(expected) then
begin
if CompareText(Copy(param, 1, Length(expected)), expected) = 0 then
begin
Result := True;
end;
end;
end;
function GetParameter(expectedParam: String): String;
var
i : LongInt;
begin
Result := '';
for i := 0 to ParamCount() do
begin
if CompareParameter(ParamStr(i), '/' + expectedParam + '=') then
begin
Result := Copy(ParamStr(i), Length(expectedParam) + 3, Length(ParamStr(i)));
break;
end;
end;
end;
procedure InitializeWizard();
var
newInstallFolder: String;
begin
newInstallFolder := GetParameter('INSTALL_FOLDER');
if Length(newInstallFolder) > 2 then
begin
if Copy(newInstallFolder, 1, 1) = '"' then
newInstallFolder := Copy(newInstallFolder, 2, Length(newInstallFolder) - 2)
if Length(newInstallFolder) > 1 then
WizardForm.DirEdit.Text := newInstallFolder;
end;
end;
The setup app is being started from another setup, in silent mode. It worked OK for me.