How to set my own variable in [Setup] and read it from [Code] - inno-setup

I'm trying to create a script in Inno Setup to pull files from a GitHub repository, extract it, and write it to a directory defined in the [Setup] section.
[Setup]
MyVariable={userappdata}\MetaQuotes\Terminal\{#MyAppName}
[Code]
procedure CurStepChanged(CurStep: TSetupStep);
begin
// Copy the selected files to the selected directory
if not FileCopy(Output + '\file1.txt', MyVariable + '\file1.txt', False) then
begin
MsgBox('Failed to copy file1.txt to the selected directory.', mbError, MB_OK);
Abort();
end;
if not FileCopy(Output + '\file2.txt', MyVariable + '\file2.txt', False) then
begin
MsgBox('Failed to copy file2.txt to the selected directory.', mbError, MB_OK);
Abort();
end;
end;
Obviously, this won't compile because MyVariable hasn't been defined in the Pascal script. Only, I'm not sure how to reference the value in [Setup]. Or am I going about this in the wrong way?

Well, you can read the [Setup] section directives with SetupSetting preprocessor function:
How to read a [Setup] parameter in the Pascal code?
But you cannot invent your own new directives.
But you can either:
Use preprocessor variable using #define directive (the way you already use it for MyAppName):
#define MyVariable "{userappdata}\MetaQuotes\Terminal\" + MyAppName
It does not matter where you place the #define as long as it it before you use the variable.
Use it like this in the Pascal Script:
ExpandConstant('{#MyVariable}\file1.txt')
The ExpandConstant is to expand the {userappdata}.
Use Pascal Script constant:
[Code]
const
MyVariable = '{userappdata}\MetaQuotes\Terminal\{#MyAppName}';
Use it like any other Pascal Script variable/constant.
ExpandConstant(MyVariable) + '\file1.txt'

Related

What does it mean that message boxes are being suppressed in Inno Setup?

Here's a page from documentation: SuppressibleMsgBox.
What does it mean If message boxes are being suppressed...?
There's a link to the explanation right after the part you have quoted:
If message boxes are being suppressed (see Setup Command Line Parameters), Default is returned.
In the link, there's /SUPPRESSMSGBOXES commandline parameter documented:
Instructs Setup to suppress message boxes. Only has an effect when combined with '/SILENT' or '/VERYSILENT'.
So normally, the SuppressibleMsgBox behaves as the MsgBox. But if you run the installer with the /SUPPRESSMSGBOXES parameter, the SuppressibleMsgBox does nothing, only silently returns the value of the Default parameter.
A practical example of use of the function:
function NextButtonClick(CurPageID: Integer): Boolean;
var
Dir: string;
Msg: string;
begin
Result := True;
if CurPageID = wpSelectDir then
begin
Dir := WizardForm.DirEdit.Text;
if Pos(' ', Dir) > 0 then
begin
Msg :=
'It is not recommended to install the application to a path with spaces. '
+ 'Do you want to continue anyway?';
if SuppressibleMsgBox(Msg, mbInformation, MB_YESNO, IDYES) = IDNO then
begin
Result := False;
end;
end;
end;
end;
In an interactive installation, the installer will warn, if the user tries to install to a path with spaces. But if you are automating a silent installation with /SILENT /SUPPRESSMSGBOXES, the installer will proceed.
It's good idea to use SuppressibleMsgBox whenever you do not want that particular message to break silent installation. So for most cases.

Inno Setup Sometimes Fails to Change File During Installation

During an Inno Setup install I am adding files to a zip archive, see here: Inno Setup Copy Files and Folders to an Existing Zip File. I immediately after adding the files, the archive is renamed by changing the file extension from .zip to .doc.
Code used to rename is:
RenameFile(ExpandConstant('{app}\MyFile.zip'), expandconstant('{app}\MyFile.doc'))
While this used to work well under windows 7 and 8, it has become less reliable and only sometimes works under windows 10.
Note, things I have tried include:
adding sleep(###); intervals but this does not work...
copying the file with a different name as per the accepted answer: Is it possible to move existing directories/files with an INNO script?
Looking for suggestions to make a robust solution and or debugging tips.
[Edit: added the codes... have renamed some bits to make it easier to read]
function SetFileAttributes(lpFileName : String; dwAttribs : LongInt) : Boolean;
external 'SetFileAttributesA#kernel32.dll stdcall';
procedure RepackZip();
var
ResultCode, i: Integer;
x1, x2: string;
begin
// Find files
x1 := FindFile('xmlns="sl:SLA"');
x2 := FindFile('xmlns="sl:SLB"');
log(ExpandConstant('{app}'));
// 2. Copy files to archive
SetFileAttributes ((expandconstant('{app}\MyFile.zip')), 0);
if not FileCopy(ExpandConstant('{tmp}\SLA.xml'), ExpandConstant('{app}\Temp\customXml\') + x1, False) then
MsgBox(x1 + 'failed!', mbError, MB_OK);
if not FileCopy(ExpandConstant('{tmp}\SLB.xml'), ExpandConstant('{app}\Temp\customXml\') + x2, False) then
MsgBox(x2 + 'failed!', mbError, MB_OK);
CopyToArchive();
SetFileAttributes ((expandconstant('{app}\MyFile.zip')), 0);
sleep(100);
// HAVE TRIED COPY & RENAME
// Everything works up to here and both FileCopy and FileRename fail on the same computers (permissions?)
// Have told Inno to Require Admin, makes no difference.
//RenameFile(ExpandConstant('{app}\MyFile.zip'), expandconstant('{app}\MyFile.dotm'))
FileCopy(ExpandConstant('{app}\MyFile.zip'), ExpandConstant('{app}\MyFile.doc'), false);
For i := 0 to 5 do
begin
if not FileExists(ExpandConstant('{app}\MyFile.doc')) then
begin
sleep (250);
end
else begin
// SetFileAttributes ((expandconstant('{app}\MyFile.doc')), 1);
exit;
end;
end;
if not FileExists(expandconstant('{app}\MyFile.doc')) then
MsgBox('Failed - rename archive to .doc', mbError, MB_OK);
end;
And CopyToArchive (this works - but I was wondering if CopyToArchive might somehow be holding the archive open and preventing the rename):
procedure CopyToArchive(); //(const Archive, Content: string);
var
Shell: Variant;
Folder: Variant;
Archive, Content: string;
objFSO, h: Variant;
max0, max1: integer;
begin
Shell := CreateOleObject('Shell.Application');
Archive := ExpandConstant('{app}') + '\MyFile.zip';
Folder := Shell.NameSpace(Archive);
log('Archive Location: ' + Archive);
objFSO := CreateOleObject('Scripting.FileSystemObject');
h := objFSO.getFile(Archive);
Content := ExpandConstant('{app}\Temp\customXml\');
Folder.CopyHere(Content, $0100);
sleep(2000);
end;
One thing that I started looking into was to use objFSO to rename the Archive, but I was unable to figure it out...
There are two problems:
.CopyHere call is asynchronous. After you call it, you have to wait for the archiving to complete. While Sleep is not really realiable, it should do. .CopyHere actually does not lock the file, so it won't prevent the rename, but you may end up renaming an incomplete file.
What causes the rename to fail is your call to objFSO.getFile(Archive), which locks the file and you never unlock it. And you actually never use h. So remove that call.
Why don't you rename the file before archiving? It would prevent all these problems.

Use two/multiple selected directories from custom page in Files section

I need create custom page of two destination.
I've done:
#define MyAppName "TESTPROG"
[Setup]
AppName={#MyAppName}
DefaultDirName=C:\test\{#MyAppName}
DefaultGroupName={#MyAppName}
[Code]
var
Page: TInputDirWizardPage;
DataDir: String;
procedure InitializeWizard;
begin
Page := CreateInputDirPage(wpWelcome,
'Select Personal Data Location', 'Where should personal data files be stored?',
'Personal data files will be stored in the following folder.'#13#10#13#10 +
'To continue, click Next. ' +
'If you would like to select a different folder, click Browse.',
False, 'New Folder');
Page.Add('Local APP');
Page.Add('Local Storage');
Page.Values[0] := ('C:\My Program');
Page.Values[1] := ('D:\My Program');
DataDir := Page.Values[0];
end;
I need to know how and where I set DefaultDirName with Page.Values[0] and Page.Values[1]
I need it because some part of my files will be in a folder and others in other folder.
For example:
[Files]
Source: C:\TEST\DLL1.bat; DestDir: Page.Values[0]\sys1;
Source: C:\TEST\DLL2.bat; DestDir: Page.Values[1]\sys2;
Use a scripted constant:
[Files]
Source: C:\TEST\DLL1.bat; DestDir: "{code:GetDir|0}\sys1"
Source: C:\TEST\DLL2.bat; DestDir: "{code:GetDir|1}\sys2"
[Code]
var
Page: TInputDirWizardPage;
function GetDir(Param: string): string;
begin
Result := Page.Values[StrToInt(Param)];
end;
procedure InitializeWizard;
begin
Page := CreateInputDirPage(...);
...
end;
If you want to use one of the (the first) paths from the TInputDirWizardPage instead of the path from "Select Destination Location" page, you have three options.
Disable the "Select Destination Location" page using DisableDirPage directive:
DisableDirPage=yes
Copy the path from the TInputDirWizardPage to the hidden "Select
Destination Location" page, when the user presses Next button:
var
Page: TInputDirWizardPage;
function InputDirPageNextButtonClick(Sender: TWizardPage): Boolean;
begin
{ Use the first path as the "destination path" }
WizardForm.DirEdit.Text := Page.Values[0];
Result := True;
end;
procedure InitializeWizard();
begin
Page := CreateInputDirPage(...);
...
Page.OnNextButtonClick := #InputDirPageNextButtonClick;
end;
To complement that you may also consider copying the initial WizardForm.DirEdit to your custom box. This way you make sure that 1) on re-install/upgrade, the previously selected value is reused; 2) /DIR command-line switch works. For that see How to make Inno Setup /DIR command line switch work with custom path page.
Replace all uses of the {app} constant with {code:GetDir|0}.
Make Inno Setup not create the {app} path using CreateAppDir directive:
CreateAppDir=no
(this implies DisableDirPage=yes).
And have the uninstall files be stored in the first path using UninstallFilesDir directive:
UninstallFilesDir={code:GetDir|0}
Contrary to 1), with this approach the previous installation path won't get reused for the later upgrade/re-install. To implement that see Inno Setup Prompt user for a folder and store the value.
Do not use the CreateInputDirPage, but rather add a second path input box on the "Select Destination Location" page (SelectDirPage).

GetComputerNameString Inno Setup

I have a line of text in my Inno Setup file that is:
TextBox.Text := GetComputerNameString();
to get the computers name. I'm getting this error when trying to go through the setup wizard once it's built:
Do I have to do some sort of code setup (like registering an external function or something) to call this function or should I just be able to call it since it's built in?
You declare a variable as a global variable
[code]
var
glbComputerName String;
...
step by 1 function
glbComputerName := GetComputerNameString();
TextBox.Text := glbComputerName;
...
step by 2 function
//glbComputerName use...
MsgBox( 'Computer Name :' + glbComputerName, mbError, MB_OK );

WizardForm.DirEdit.Text not updating properly. Inno setup

I am trying to set the path in the 'choose install directory' form using INNO setup. Here is my code
procedure CurPageChanged(pageID: Integer);
var
sInstallDir: String;
begin
// Default install dir is the IIS install path
if (pageID = wpSelectDir) then begin
sInstallDir := GetIISInstallPath + '\MyFolder';
Log('GetIISInstallPath: '+ GetIISInstallPath);
Log('sInstallDir: ' + sInstallDir);
WizardForm.DirEdit.Text := sInstallDir;
end;
end;
The problem I am having is that 'GetIISInstallPath' returns me 'c:\inetpub\wwwroot and that is what I see in the WizardForm. It seems to not add the MyFolder bit.
I printed out the involved variables and they all have the correct value.
sInstallDir shows up as 'C:\inetpub\wwwroot\MyFolder' but it does not show in the text field. It shows (as mentioned) only 'C:\inetpub\wwwroot'.
Please advise.
Thank You
Your code works fine for me but, can I suggest you to use
[Setup]
...
DefaultDirName={code:GetDefaultDirName}
[code]
...
function GetDefaultDirName(): String;
begin
Result := GetIISInstallPath + '\MyFolder';
end;
Doing this the "GetIISInstallPath + \MyFolder" will be your default directory

Resources