Inno Setup - suppress "Setup command line:" log entry - inno-setup

We are using Inno Setup(version 5.4.2) as the packaging tool to generate our installer. We are passing some password values as command line arguments. In Inno Setup all command line arguments are logging into installation log automatically with "Setup command line:" entry. Is any way to suppress the "Setup Command Line" logging into log.

No. You can disable only the overall logging. Here's the excerpt from the relevant part of the source code:
...
Log('Setup version: ' + SetupTitle + ' version ' + SetupVersion);
Log('Original Setup EXE: ' + SetupLdrOriginalFilename);
Log('Setup command line: ' + GetCmdTail);
LogWindowsVersion;
...
As you can see there is no condition before the Log procedure call for the command line tail as well as the Log procedure itself does not contain a way to filter out certain log messages. So, at this time the only way to prevent this potential security issue is disabling the overall logging .
Since this might be a security issue, I would suggest you to file a feature request report.

As an alternative, you could ask the user to provide an INI file via the command line instead:
config.ini
[credentials]
username=foo
password=bar
command line invocation
setup.exe /CONFIGFILE=config.ini
PascalScript
Note that some of this is untested, please use it at your own risk.
function CommandLineSwitchIsPresent(Param : String): Boolean;
var
i: Integer;
begin
Result := False;
if not StartsWith('/', Param) then
begin
Param := '/' + Param;
end;
for i:= 0 to ParamCount do
begin
if (CompareText(Param, ParamStr(i)) = 0)
or StartsWith(Param + '=', ParamStr(i)) then
begin
Result := True;
break;
end;
end;
end;
function GetCommandlineParam(Param: String; var OutStr: String): Boolean;
var
ParamNameAndValue: String;
i: Integer;
j: Integer;
begin
Result := False;
Param := Param + '=';
if not StartsWith('/', Param) then
begin
Param := '/' + Param;
end;
for i := 0 to ParamCount do
begin
ParamNameAndValue := ParamStr(i);
if StartsWith(AnsiUppercase(Param), AnsiUppercase(ParamNameAndValue)) then
begin
for j := 0 to Length(ParamNameAndValue) do
begin
if j > Length(Param) then
begin
OutStr := OutStr + ParamNameAndValue[j];
end;
end;
Result := True;
break;
end;
end;
end;
function GetConfig(Section: String; Key: String; ConfigFile: String): String;
begin
if IniKeyExists('credentials', Key, ConfigFile) then
begin
Result := GetIniString('credentials', Key, '', ConfigFile);
end
else begin
RaiseException(
Format(
'%s key not found in [%s] section in %s', [Key, Section, ConfigFile]
);
end;
end;
var
_gConfigFile: String;
procedure DoStuffWithPassword();
var
Username: String;
Password: String;
ShortPath: String;
ExpandedPath: String;
begin
if not GetCommandlineParam('CONFIGFILE', ShortPath) then
begin
RaiseException('CONFIGFILE parameter is required!');
end;
// Go from relative path to absolute
ExpandedPath := ExpandFileName(ShortPath);
if FileExists(ExpandedPath) then
begin
_gConfigFile := ExpandedPath;
end
else begin
RaiseException('CONFIGFILE file ' + ShortPath + ' not found!');
end;
Username := GetConfig('credentials', 'username', _gConfigFile);
Password := GetConfig('credentials', 'password', _gConfigFile);
end;

Related

inno setup: tail a log file to a multiline textbox in the wpInstalling page [duplicate]

I created an Input page that executes a command line app using the created variables from those inputs. Naturally, the cmd window pop ups on my screen. I would like to know if there is any way to embed the cmd window (or the output) on my Inno Setup installer page.
I'm running Inno Setup 5.6.1 (because of Windows XP compatibility), but I'm OK if I have to switch to the last version.
[Code]
var
MAIL: TInputQueryWizardPage;
Final: TWizardPage;
BotonIniciar: Tbutton;
procedure BotonIniciarOnClick(Sender: TObject);
begin
WizardForm.NextButton.Onclick(nil);
Exec(ExpandConstant('{tmp}\imapsync.exe'),'MAIL.Values[0]','', SW_SHOW,
ewWaitUntilTerminated, ResultCode);
end;
procedure InitializeWizard;
begin
MAIL := CreateInputQueryPage(wpWelcome, '', '', '');
MAIL.Add('Please input your information', False);
BotonIniciar := TNewButton.Create(MAIL);
BotonIniciar.Caption := 'Iniciar';
BotonIniciar.OnClick := #BotonIniciarOnClick;
BotonIniciar.Parent := WizardForm;
BotonIniciar.Left := WizardForm.NextButton.Left - 250 ;
BotonIniciar.Top := WizardForm.CancelButton.Top - 10;
BotonIniciar.Width := WizardForm.NextButton.Width + 60;
BotonIniciar.Height := WizardForm.NextButton.Height + 10;
end;
I'm might be missing some parts of the code, but I think it's understandable.
Fist I create the input page, then I create a button with the OnClick property that calls to the BotonIniciarOnClick procedure.
Actually, the code works great. But as I said I'm having a floating cmd window.
I would like to see something like this:
It's just a random image I took from google.
What I want to see is similar to a standard "show details" option on an installer.
You can redirect the command output to a file and monitor the file for changes, loading them to list box (or maybe a memo box).
var
ProgressPage: TOutputProgressWizardPage;
ProgressListBox: TNewListBox;
function SetTimer(
Wnd: LongWord; IDEvent, Elapse: LongWord; TimerFunc: LongWord): LongWord;
external 'SetTimer#user32.dll stdcall';
function KillTimer(hWnd: LongWord; uIDEvent: LongWord): BOOL;
external 'KillTimer#user32.dll stdcall';
var
ProgressFileName: string;
function BufferToAnsi(const Buffer: string): AnsiString;
var
W: Word;
I: Integer;
begin
SetLength(Result, Length(Buffer) * 2);
for I := 1 to Length(Buffer) do
begin
W := Ord(Buffer[I]);
Result[(I * 2)] := Chr(W shr 8); { high byte }
Result[(I * 2) - 1] := Chr(Byte(W)); { low byte }
end;
end;
procedure UpdateProgress;
var
S: AnsiString;
I, L, Max: Integer;
Buffer: string;
Stream: TFileStream;
Lines: TStringList;
begin
if not FileExists(ProgressFileName) then
begin
Log(Format('Progress file %s does not exist', [ProgressFileName]));
end
else
begin
try
// Need shared read as the output file is locked for writing,
// so we cannot use LoadStringFromFile
Stream :=
TFileStream.Create(ProgressFileName, fmOpenRead or fmShareDenyNone);
try
L := Stream.Size;
Max := 100*2014;
if L > Max then
begin
Stream.Position := L - Max;
L := Max;
end;
SetLength(Buffer, (L div 2) + (L mod 2));
Stream.ReadBuffer(Buffer, L);
S := BufferToAnsi(Buffer);
finally
Stream.Free;
end;
except
Log(Format('Failed to read progress from file %s - %s', [
ProgressFileName, GetExceptionMessage]));
end;
end;
if S <> '' then
begin
Log('Progress len = ' + IntToStr(Length(S)));
Lines := TStringList.Create();
Lines.Text := S;
for I := 0 to Lines.Count - 1 do
begin
if I < ProgressListBox.Items.Count then
begin
ProgressListBox.Items[I] := Lines[I];
end
else
begin
ProgressListBox.Items.Add(Lines[I]);
end
end;
ProgressListBox.ItemIndex := ProgressListBox.Items.Count - 1;
ProgressListBox.Selected[ProgressListBox.ItemIndex] := False;
Lines.Free;
end;
// Just to pump a Windows message queue (maybe not be needed)
ProgressPage.SetProgress(0, 1);
end;
procedure UpdateProgressProc(
H: LongWord; Msg: LongWord; Event: LongWord; Time: LongWord);
begin
UpdateProgress;
end;
procedure BotonIniciarOnClick(Sender: TObject);
var
ResultCode: Integer;
Timer: LongWord;
AppPath: string;
AppError: string;
Command: string;
begin
ProgressPage :=
CreateOutputProgressPage(
'Installing something', 'Please wait until this finishes...');
ProgressPage.Show();
ProgressListBox := TNewListBox.Create(WizardForm);
ProgressListBox.Parent := ProgressPage.Surface;
ProgressListBox.Top := 0;
ProgressListBox.Left := 0;
ProgressListBox.Width := ProgressPage.SurfaceWidth;
ProgressListBox.Height := ProgressPage.SurfaceHeight;
// Fake SetProgress call in UpdateProgressProc will show it,
// make sure that user won't see it
ProgressPage.ProgressBar.Top := -100;
try
Timer := SetTimer(0, 0, 250, CreateCallback(#UpdateProgressProc));
ExtractTemporaryFile('install.bat');
AppPath := ExpandConstant('{tmp}\install.bat');
ProgressFileName := ExpandConstant('{tmp}\progress.txt');
Log(Format('Expecting progress in %s', [ProgressFileName]));
Command := Format('""%s" > "%s""', [AppPath, ProgressFileName]);
if not Exec(ExpandConstant('{cmd}'), '/c ' + Command, '', SW_HIDE,
ewWaitUntilTerminated, ResultCode) then
begin
AppError := 'Cannot start app';
end
else
if ResultCode <> 0 then
begin
AppError := Format('App failed with code %d', [ResultCode]);
end;
UpdateProgress;
finally
// Clean up
KillTimer(0, Timer);
ProgressPage.Hide;
DeleteFile(ProgressFileName);
ProgressPage.Free();
end;
if AppError <> '' then
begin
// RaiseException does not work properly while
// TOutputProgressWizardPage is shown
RaiseException(AppError);
end;
end;
Above was tested with a batch file like:
#echo off
echo Starting
echo Doing A...
echo Extracting something...
echo Doing B...
echo Extracting something...
timeout /t 1 > nul
echo Doing C...
echo Extracting something...
echo Doing D...
echo Extracting something...
timeout /t 1 > nul
echo Doing E...
echo Extracting something...
echo Doing F...
echo Extracting something...
timeout /t 1 > nul
...
If you want to display the output as part of the installation process, instead of on a button click, see:
Execute a batch file after installation and display its output on a custom page before Finished page in Inno Setup

Find JSON file with specific name pattern and contents in Inno Setup

I have a working script, where the installer finds the specific file inside specific path but I need to slightly change it. I noticed that when I reinstall the application, the name of the file which I want to get the info from is different every time, so it's not that important as I thought.
There are some things which are not changing although - the path to that variable filename, which I have defined already using the constant and can be used again and also the file extension. So if the path doesn't change, the search process could be a lot more quicker just for that. The file format is JSON and code for it is already applied in the script.
Here is the JSON structure sample:
"ChunkDbs": [],
"CompatibleApps": [],
"DisplayName": "Application Name",
"InstallLocation": "D:\\Program Files (x86)\\ApplicationName",
"InstallTags": [],
"InstallComponents": [],
The only solution for this is searching all the files in specific path with specific extension. There are some variables I need to use here, the first is already mentioned in the code as InstallLocation which is the proper install path, the next variable which I need to define is DisplayName, which contains the application name and probably another one that define the file extension.
So I need to find the right file, containing the specific string inside DisplayName parameter, compare if it's the same as in the defined one and then read the installation path from InstallLocation parameter.
Here is the code I have so far:
[Setup]
DefaultDirName={code:GetInstallLocation}
[Code]
#include "JsonParser.pas"
function ParseJsonAndLogErrors(
var JsonParser: TJsonParser; const Source: WideString): Boolean;
var
I: Integer;
begin
ParseJson(JsonParser, Source);
Result := (Length(JsonParser.Output.Errors) = 0);
if not Result then
begin
Log('Error parsing JSON');
for I := 0 to Length(JsonParser.Output.Errors) - 1 do
begin
Log(JsonParser.Output.Errors[I]);
end;
end;
end;
function GetJsonRoot(Output: TJsonParserOutput): TJsonObject;
begin
Result := Output.Objects[0];
end;
function FindJsonValue(
Output: TJsonParserOutput; Parent: TJsonObject; Key: TJsonString;
var Value: TJsonValue): Boolean;
var
I: Integer;
begin
for I := 0 to Length(Parent) - 1 do
begin
if Parent[I].Key = Key then
begin
Value := Parent[I].Value;
Result := True;
Exit;
end;
end;
Result := False;
end;
function FindJsonString(
Output: TJsonParserOutput; Parent: TJsonObject; Key: TJsonString;
var Str: TJsonString): Boolean;
var
JsonValue: TJsonValue;
begin
Result :=
FindJsonValue(Output, Parent, Key, JsonValue) and
(JsonValue.Kind = JVKString);
if Result then
begin
Str := Output.Strings[JsonValue.Index];
end;
end;
function MultiByteToWideChar(
CodePage: UINT; dwFlags: DWORD; const lpMultiByteStr: AnsiString;
cchMultiByte: Integer; lpWideCharStr: string; cchWideChar: Integer): Integer;
external 'MultiByteToWideChar#kernel32.dll stdcall';
function LoadStringFromFileInCP(
FileName: string; var S: string; CP: Integer): Boolean;
var
Ansi: AnsiString;
Len: Integer;
begin
Result := LoadStringFromFile(FileName, Ansi);
if Result then
begin
Len := MultiByteToWideChar(CP, 0, Ansi, Length(Ansi), S, 0);
SetLength(S, Len);
MultiByteToWideChar(CP, 0, Ansi, Length(Ansi), S, Len);
end;
end;
const
CP_UTF8 = 65001;
var
InstallLocation: string;
<event('InitializeSetup')>
function InitializeSetupParseConfig(): Boolean;
var
Json: string;
ConfigPath: string;
JsonParser: TJsonParser;
JsonRoot: TJsonObject;
S: TJsonString;
begin
Result := True;
ConfigPath := ExpandConstant('{commonappdata}\Data\FEE8D728379C5E.dat');
Log(Format('Reading "%s"', [ConfigPath]));
if not LoadStringFromFileInCP(ConfigPath, Json, CP_UTF8) then
begin
MsgBox(Format('Error reading "%s"', [ConfigPath]), mbError, MB_OK);
Result := True;
end
else
if not ParseJsonAndLogErrors(JsonParser, Json) then
begin
MsgBox(Format('Error parsing "%s"', [ConfigPath]), mbError, MB_OK);
Result := True;
end
else
begin
JsonRoot := GetJsonRoot(JsonParser.Output);
if not FindJsonString(JsonParser.Output, JsonRoot, 'InstallLocation', S) then
begin
MsgBox(Format('Cannot find InstallLocation in "%s"', [ConfigPath]),
mbError, MB_OK);
Result := False;
end
else
begin
InstallLocation := S;
Log(Format('Found InstallLocation = "%s"', [InstallLocation]));
end;
ClearJsonParser(JsonParser);
end;
end;
function GetInstallLocation(Param: string): string;
begin
Result := InstallLocation;
end;
Thank you for help
I've written my code prototype to show how I see the code to make what I want (the most intuitive / clearest way):
const
MyDisplayName = 'MyReqAppName';
MyPath = 'C:\some\path\*.dat';
var
RealDisplayName: string;
InstallLocation: string;
function FindParameters();
begin
RealDisplayName := 'DisplayName';
InstallLocation := 'InstallLocation';
Find(RealDisplayName in MyPath);
if (RealDisplayName = MyDisplayName) then
Find(InstallLocation);
else
repeat
Find(RealDisplayName in MyPath);
until(RealDisplayName = MyDisplayName);
end;
To find a files with a specific extension, use FindFirst and FindNext functions.
For each matching file you find, inspect its contents using the code you already have.
You will need to adapt the code further, as it's not clear to me what you want to do in case of various kinds of errors.
var
Path: string;
FindRec: TFindRec;
Json: string;
ConfigPath: string;
JsonParser: TJsonParser;
JsonRoot: TJsonObject;
S: TJsonString;
DisplayName: string;
begin
Path := 'C:\some\path';
if FindFirst(Path + '\*.dat', FindRec) then
begin
repeat
Log('Found: ' + FindRec.Name);
ConfigPath := Path + '\' + FindRec.Name;
Log(Format('Reading "%s"', [ConfigPath]));
if not LoadStringFromFileInCP(ConfigPath, Json, CP_UTF8) then
begin
Log(Format('Error reading "%s"', [ConfigPath]));
end
else
if not ParseJsonAndLogErrors(JsonParser, Json) then
begin
Log(Format('Error parsing "%s"', [ConfigPath]));
end
else
begin
JsonRoot := GetJsonRoot(JsonParser.Output);
if not FindJsonString(JsonParser.Output, JsonRoot, 'DisplayName', S) then
begin
Log(Format('Cannot find DisplayName in "%s"', [ConfigPath]));
end
else
if DisplayName <> MyDisplayName then
begin
Log(Format('DisplayName is "%s", not what we want', [DisplayName]));
end
else
begin
Log(Format('DisplayName is "%s", what we want', [DisplayName]));
if not FindJsonString(
JsonParser.Output, JsonRoot, 'InstallLocation', S) then
begin
Log(Format('Cannot find InstallLocation in "%s"', [ConfigPath]));
end
else
begin
InstallLocation := S;
Log(Format('Found InstallLocation = "%s"', [InstallLocation]));
end;
end;
ClearJsonParser(JsonParser);
end;
until not FindNext(FindRec);
FindClose(FindRec);
end;
end;

Inno setup: check for new updates

After reading this post How to read a text file from the Internet resource?, I've adapted the code to what I need but I have some problems.
What I want to do is that when I run the setup, it checks for new updates. 1) If there isn't a new update, don't show any message. 2) And if there is a new update, show a message asking whether you want to download it or not.
This is my code:
procedure InitializeWizard;
var
DxLastVersion: string;
DxSetupVersion: String;
begin
if DownloadFile('http://dex.wotanksmods.com/latestver.txt', DxLastVersion) then
MsgBox(DxLastVersion, mbInformation, MB_YESNO)
else
MsgBox(DxLastVersion, mbError, MB_OK)
end;
Thanks so much in advanced.
Since you've decided to use a common version string pattern, you'll need a function which will parse and compare a version string of your setup and the one downloaded from your site. And because there is no such function built-in in Inno Setup, you'll need to have your own one.
I've seen a few functions for comparing version strings, like e.g. the one used in this script, but I've decided to write my own. It can detect an invalid version string, and treats the missing version chunks as to be 0, which causes comparison of version strings like follows to be equal:
1.2.3
1.2.3.0.0.0
The following script might do what you want (the setup version is defined by the AppVersion directive):
[Setup]
AppName=My Program
AppVersion=1.2.3
DefaultDirName={pf}\My Program
[Code]
const
SetupURL = 'http://dex.wotanksmods.com/setup.exe';
VersionURL = 'http://dex.wotanksmods.com/latestver.txt';
type
TIntegerArray = array of Integer;
TCompareResult = (
crLesser,
crEquals,
crGreater
);
function Max(A, B: Integer): Integer;
begin
if A > B then Result := A else Result := B;
end;
function CompareValue(A, B: Integer): TCompareResult;
begin
if A = B then
Result := crEquals
else
if A < B then
Result := crLesser
else
Result := crGreater;
end;
function AddVersionChunk(const S: string; var A: TIntegerArray): Integer;
var
Chunk: Integer;
begin
Chunk := StrToIntDef(S, -1);
if Chunk <> -1 then
begin
Result := GetArrayLength(A) + 1;
SetArrayLength(A, Result);
A[Result - 1] := Chunk;
end
else
RaiseException('Invalid format of version string');
end;
function ParseVersionStr(const S: string; var A: TIntegerArray): Integer;
var
I: Integer;
Count: Integer;
Index: Integer;
begin
Count := 0;
Index := 1;
for I := 1 to Length(S) do
begin
case S[I] of
'.':
begin
AddVersionChunk(Copy(S, Index, Count), A);
Count := 0;
Index := I + 1;
end;
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
begin
Count := Count + 1;
end;
else
RaiseException('Invalid char in version string');
end;
end;
Result := AddVersionChunk(Copy(S, Index, Count), A);
end;
function GetVersionValue(const A: TIntegerArray; Index,
Length: Integer): Integer;
begin
Result := 0;
if (Index >= 0) and (Index < Length) then
Result := A[Index];
end;
function CompareVersionStr(const A, B: string): TCompareResult;
var
I: Integer;
VerLenA, VerLenB: Integer;
VerIntA, VerIntB: TIntegerArray;
begin
Result := crEquals;
VerLenA := ParseVersionStr(A, VerIntA);
VerLenB := ParseVersionStr(B, VerIntB);
for I := 0 to Max(VerLenA, VerLenB) - 1 do
begin
Result := CompareValue(GetVersionValue(VerIntA, I, VerLenA),
GetVersionValue(VerIntB, I, VerLenB));
if Result <> crEquals then
Exit;
end;
end;
function DownloadFile(const URL: string; var Response: string): Boolean;
var
WinHttpRequest: Variant;
begin
Result := True;
try
WinHttpRequest := CreateOleObject('WinHttp.WinHttpRequest.5.1');
WinHttpRequest.Open('GET', URL, False);
WinHttpRequest.Send;
Response := WinHttpRequest.ResponseText;
except
Result := False;
Response := GetExceptionMessage;
end;
end;
function InitializeSetup: Boolean;
var
ErrorCode: Integer;
SetupVersion: string;
LatestVersion: string;
begin
Result := True;
if DownloadFile(VersionURL, LatestVersion) then
begin
SetupVersion := '{#SetupSetting('AppVersion')}';
if CompareVersionStr(LatestVersion, SetupVersion) = crGreater then
begin
if MsgBox('There is a newer version of this setup available. Do ' +
'you want to visit the site ?', mbConfirmation, MB_YESNO) = IDYES then
begin
Result := not ShellExec('', SetupURL, '', '', SW_SHOW, ewNoWait,
ErrorCode);
end;
end;
end;
end;

how to pass a variable value to an xml file?

this is my code to enter a port number from user.upon installing i want to get the port number changed in apache tomcat server.xml file.
Iam passing apache tomcat zip file also using files section and unzip it in run section
var
javaVersion: String;
javaPath: String;
//port number code
function SetFocus(hWnd: HWND): HWND;
external 'SetFocus#user32.dll stdcall';
var
SerialPage: TWizardPage;
SerialEdits: array of TEdit;
const
CF_TEXT = 1;
VK_BACK = 8;
SC_EDITCOUNT = 1;
SC_CHARCOUNT = 4;
procedure OnSerialEditChange(Sender: TObject);
var
I: Integer;
CanContinue: Boolean;
begin
CanContinue := True;
for I := 0 to GetArrayLength(SerialEdits) - 1 do
if Length(SerialEdits[I].Text) < SC_CHARCOUNT then
begin
CanContinue := False;
Break;
end;
WizardForm.NextButton.Enabled := CanContinue;
end;
function GetSerialNumber(Param: String): string;
var
I: Integer;
begin
Result := '';
for I := 0 to GetArrayLength(SerialEdits) - 1 do
Result := Result + SerialEdits[I].Text ;
end;
procedure OnSerialEditKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
var
Edit: TEdit;
EditIndex: Integer;
begin
Edit := TEdit(Sender);
EditIndex := Edit.TabOrder - SerialEdits[0].TabOrder;
if (EditIndex = 0) and (Key = Ord('V')) and (Shift = [ssCtrl]) then
begin
if TryPasteSerialNumber then
Key := 0;
end
else
if (Key >= 32) and (Key <= 255) then
begin
if Length(Edit.Text) = SC_CHARCOUNT - 1 then
begin
if EditIndex < GetArrayLength(SerialEdits) - 1 then
SetFocus(SerialEdits[EditIndex + 1].Handle)
else
SetFocus(WizardForm.NextButton.Handle);
end;
end
else
if Key = VK_BACK then
if (EditIndex > 0) and (Edit.Text = '') and (Edit.SelStart = 0) then
SetFocus(SerialEdits[EditIndex - 1].Handle);
end;
procedure CreateSerialNumberPage;
var
I: Integer;
Edit: TEdit;
DescLabel: TLabel;
EditWidth: Integer;
begin
SerialPage := CreateCustomPage(wpWelcome, 'Serial number validation',
'Enter the valid serial number');
DescLabel := TLabel.Create(SerialPage);
DescLabel.Top := 16;
DescLabel.Left := 0;
DescLabel.Parent := SerialPage.Surface;
DescLabel.Caption := 'Enter the valid serial number and continue with the installation...';
DescLabel.Font.Style := [fsBold];
SetArrayLength(SerialEdits, SC_EDITCOUNT);
EditWidth := (SerialPage.SurfaceWidth - ((SC_EDITCOUNT - 1) * 8)) div SC_EDITCOUNT;
for I := 0 to SC_EDITCOUNT - 1 do
begin
Edit := TEdit.Create(SerialPage);
Edit.Top := 40;
Edit.Left := I * (EditWidth + 8);
Edit.Width := EditWidth;
Edit.CharCase := ecUpperCase;
Edit.MaxLength := SC_CHARCOUNT;
Edit.Parent := SerialPage.Surface;
Edit.OnChange := #OnSerialEditChange;
Edit.OnKeyDown := #OnSerialEditKeyDown;
SerialEdits[I] := Edit;
end;
end;
procedure CurPageChanged(CurPageID: Integer);
begin
if CurPageID = SerialPage.ID then
WizardForm.NextButton.Enabled := False;
end;
procedure InitializeWizard;
begin
CreateSerialNumberPage;
end ;
i want to replace the port number which was entered by user in tomcats server.xml using tokens
<Connector port="##portnumber##" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
Here's a script I've made for you. I've changed the way of entering port number and shown how to modify attribute values in XML files. Also notice the usage of the AfterInstall function:
#define TomcatDest "{app}\tomcat"
#define TomcatFullPath TomcatDest + "\apache-tomcat-7.0.42"
#define TomcatSrvConfigFile TomcatFullPath + "\conf\server.xml"
[Setup]
AppName=My Program
AppVersion=1.5
DefaultDirName={pf}\My Program
[Files]
Source: "unzip.exe"; DestDir: "{tmp}\installertemps"
Source: "apache-tomcat-7.0.42.zip"; DestDir: "{tmp}\installertemps"
[Run]
Filename: "{tmp}\installertemps\unzip.exe"; Parameters: " ""{tmp}\installertemps\apache-tomcat-7.0.42.zip"" -d ""{#TomcatDest}"" "; AfterInstall: UpdateConfigFile(ExpandConstant('{#TomcatSrvConfigFile}'))
[Code]
const
DefaultPort = 8080;
var
ConfigPage: TInputQueryWizardPage;
procedure SaveAttrValueToXML(const FileName, NodePath, Attribute,
Value: string);
var
XMLNode: Variant;
XMLDocument: Variant;
begin
XMLDocument := CreateOleObject('Msxml2.DOMDocument');
try
XMLDocument.async := False;
XMLDocument.load(FileName);
if (XMLDocument.parseError.errorCode <> 0) then
MsgBox('The XML file could not be parsed. ' +
XMLDocument.parseError.reason, mbError, MB_OK)
else
begin
XMLDocument.setProperty('SelectionLanguage', 'XPath');
XMLNode := XMLDocument.selectSingleNode(NodePath);
XMLNode.setAttribute(Attribute, Value);
XMLDocument.save(FileName);
end;
except
MsgBox('An error occured!' + #13#10 + GetExceptionMessage,
mbError, MB_OK);
end;
end;
procedure InitializeWizard;
begin
ConfigPage := CreateInputQueryPage(wpSelectDir, 'Tomcat configuration',
'Description', 'SubCaption');
ConfigPage.Add('Port:', False);
ConfigPage.Values[0] := IntToStr(DefaultPort);
end;
function NextButtonClick(CurPageID: Integer): Boolean;
var
PortNumber: Integer;
begin
Result := True;
if CurPageID = ConfigPage.ID then
begin
PortNumber := StrToIntDef(ConfigPage.Values[0], -1);
// modify the statement to allow users enter only valid port numbers;
// currently the value of -1 means that there is not even a number entered
// in the edit box
if (PortNumber = -1) then
begin
Result := False;
MsgBox('You''ve entered invalid port number. The setup cannot continue...', mbError, MB_OK);
end;
end;
end;
procedure UpdateConfigFile(const FileName: string);
begin
SaveAttrValueToXML(FileName, '//Server/Service/Connector', 'port',
ConfigPage.Values[0]);
end;

Inno Setup - How to edit a specific line from a text file during setup?

I need to edit a specific line from a text file using Inno Setup. I need my installer to find this line ("appinstalldir" "C:MYXFOLDER\\apps\\common\\App70") and use the directory path from the installer.
This is the code I am trying to use:
procedure CurStepChanged(CurStep: TSetupStep);
begin
if CurStep = ssDone then
begin
SaveStringToFile(
ExpandConstant('{app}\app70.txt'),
'directory's path' + '\\apps\\common\\App70', True);
end;
end;
This is my text file:
"App"
{
"appID" "70"
{
"appinstalldir" "C:MYXFOLDER\\apps\\common\\App70"
}
}
This code can do it. But note, that this code doesn't check, if the value for the tag is enclosed by quote chars, once it finds a tag specified by TagName parameter, it cuts off the rest of the line and appends the value given by TagValue parameter:
function ReplaceValue(const FileName, TagName, TagValue: string): Boolean;
var
I: Integer;
Tag: string;
Line: string;
TagPos: Integer;
FileLines: TStringList;
begin
Result := False;
FileLines := TStringList.Create;
try
Tag := '"' + TagName + '"';
FileLines.LoadFromFile(FileName);
for I := 0 to FileLines.Count - 1 do
begin
Line := FileLines[I];
TagPos := Pos(Tag, Line);
if TagPos > 0 then
begin
Result := True;
Delete(Line, TagPos + Length(Tag), MaxInt);
Line := Line + ' "' + TagValue + '"';
FileLines[I] := Line;
FileLines.SaveToFile(FileName);
Break;
end;
end;
finally
FileLines.Free;
end;
end;
procedure CurStepChanged(CurStep: TSetupStep);
var
NewPath: string;
begin
if CurStep = ssDone then
begin
NewPath := ExpandConstant('{app}') + '\apps\common\App70';
StringChangeEx(NewPath, '\', '\\', True);
if ReplaceValue(ExpandConstant('{app}\app70.txt'), 'appinstalldir',
NewPath)
then
MsgBox('Tag value has been replaced!', mbInformation, MB_OK)
else
MsgBox('Tag value has not been replaced!.', mbError, MB_OK);
end;
end;

Resources