Inno Setup - How to read an INF file during the Setup - inno-setup

i need to know how to read a value from INF file [.inf], during the setup. I want the installer to check the version of the program that i am going to update, This program version is not stored in the registry or any other file, is only in the .inf file. Then is a must to get the version from it.
I got your answers, #Tlama and i cannot use a DLL to get version of the software.
This program only save the current version in the INF file.
What i want to do, is to make the installer to check the current versión of the software that i am working with, and display that version in a label text.
The inf information is this:
NetVersion=1.1.1.1
PatchVersion=2.0.1
ProductName=SoftwareX
I just need the PatchVersion to display after where it says version: #### :
this is the code i am trying to fix:
function GetInfsam: String;
var
sVersion : String;
Begin
sVersion := '';
GetIniString('', 'PatchVersion', 'sVersion', '{app}\Sam.inf');
Result := sVersion;
end;
Procedure InitializeWizard7();
var
L2Ver1 : Tlabel;
L2Ver2 : Tlabel;
Begin
L2Ver1:= TLabel.Create(WizardForm);
L2Ver1.Transparent:= True;
L2Ver1.AutoSize:= False;
L2Ver1.WordWrap:= True;
L2Ver1.Font.name:= 'Agency FB';
L2Ver1.Font.Size:= 12;
L2Ver1.Font.Color:= clwhite;
L2Ver1.Caption:= 'Version:';
L2Ver1.Parent:= WizardForm.SelectdirPage;
L2Ver1.Left := 5;
L2Ver1.top := 260;
L2Ver1.Width := 150;
L2Ver1.Height := 40;
L2Ver2:= TLabel.Create(WizardForm);
L2Ver2.Transparent:= True;
L2Ver2.AutoSize:= False;
L2Ver2.WordWrap:= True;
L2Ver2.Font.name:= 'Agency FB';
L2Ver2.Font.Size:= 12;
L2Ver2.Font.Color:= clwhite;
L2Ver2.Caption:= GetInfsam;
L2Ver2.Parent:= WizardForm.SelectdirPage;
L2Ver2.Left := L2Ver1.Width + L2Ver1.Left + 8;
L2Ver2.top := 260;
L2Ver2.Width := 100;
L2Ver2.Height := 40;
End;
Please, i need help to fix my code.

How to read INF file ?
INF files are just sort of INI files with the specified syntax. So to work with INF files you need to treat them as ordinary INI files. Assuming you have a INF file like this:
[Add.Code]
File.dll=File.dll
[File.dll]
File=http://www.code.com/file.dll
FileVersion=1,0,0,143
You can read the FileVersion key by using GetIniString this way:
procedure InitializeWizard;
var
Version: string;
begin
Version := GetIniString('File.dll', 'FileVersion', '', 'c:\File.inf');
if Version <> '' then
MsgBox('File version: ' + Version, mbInformation, MB_OK);
end;
Update:
1. Malformed INF file
According to your update, if the content of your INF file looks like this:
NetVersion=1.1.1.1
PatchVersion=2.0.1
ProductName=SoftwareX
then it's not a well formed INF file, but a name value pair text file saved with INF extension. Real INF files must have a valid [] section for each key value set, but this section is missing in your file.
2. GetIniString function cannot be called with empty Section parameter value
You must not call the GetIniString function with empty Section parameter value, because for the internally called GetPrivateProfileString function it means to return all section names for a given file, not value of a specified key. So for instance the following call is invalid, because the first parameter Section cannot be empty:
GetIniString('', 'KeyName', 'Default', 'c:\File.xxx');
3. How to work with a name value pair text file ?
You'll just need to work with that file as with a text file. For a key value text file handling would be ideal to use the TStringList class, or at least in Delphi. In InnoSetup unfortunately the TStringList class doesn't have published properties needed for a key value content manipulation, so you'll need to make your own key value text file parsing function. Here's the one for getting value for a given key. As the key value delimiter is supposed to be the = sign. This function returns a key value when succeed to find a AKeyName key in a given AFileName file or default ADefault value when fails:
function GetKeyValue(const AKeyName, AFileName, ADefault: string): string;
var
I: Integer;
KeyPos: Integer;
KeyFull: string;
FileLines: TArrayOfString;
begin
Result := ADefault;
if LoadStringsFromFile(AFileName, FileLines) then
begin
KeyFull := AKeyName + '=';
for I := 0 to GetArrayLength(FileLines) - 1 do
begin
FileLines[I] := TrimLeft(FileLines[I]);
KeyPos := Pos(KeyFull, FileLines[I]);
if KeyPos > 0 then
begin
Result := Copy(FileLines[I], KeyPos + Length(AKeyName) + 1, MaxInt);
Break;
end;
end;
end;
end;
To read a value of the PatchVersion key from the Sam.inf file expected in the currently selected install path you can use something like this. This script will update the label value whenever your user change the text in the directory edit box and when the directory selection page is going to be displayed:
var
// target version label must be declared globally
L2Ver2: TLabel;
procedure DirEditChange(Sender: TObject);
var
FilePath: string;
begin
// assign the expected INF file path
FilePath := AddBackslash(WizardForm.DirEdit.Text) + 'Sam.inf';
// read the PatchVersion key value, return N/A if not found
L2Ver2.Caption := GetKeyValue('PatchVersion', FilePath, 'N/A');
end;
procedure InitializeWizard;
begin
// create the target label as before
L2Ver2 := TLabel.Create(WizardForm);
...
// bind the DirEditChange method to the directory edit's OnChange event
WizardForm.DirEdit.OnChange := #DirEditChange;
end;
procedure CurPageChanged(CurPageID: Integer);
begin
// if the page has been turned to the select directory page, update the
// label caption by firing the assigned OnChange event method manually
if (CurPageID = wpSelectDir) then
DirEditChange(nil);
end;

All my INF files have a structure similar to INI files.
If that's your case too I suggest you to open your INF as if it was INI, using (as suggested by TLama)
in setup functions:
function GetIniInt(const Section, Key: String; const Default, Min, Max: Longint; const Filename: String): Longint;
function GetIniString(const Section, Key, Default, Filename: String): String;
in preprocessor:
str ReadIni(str 1, str 2, str 3, str? 4)
Description (from Inno Setup help)
Reads the value from an INI file. Argument 1 must be the name of the INI file, argument 2 – the name of a section in the INI file, the third argument is the key in the section to read. Last optional argument can be used to provide the default value that will be returned on failure, if it is omitted, an empty string is returned.

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.

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

InnoSetup - Set DefaultDirName at runtime

I have an application that needs to allow for up to three simultaneous installations on the same machine.
For some reason, the following code behaves as if UsePreviousAppDir is set to yes. The second time I install the app, the path ends up mangled.
The value I want to see is
C:\Our App\install_x where x corresponds to the user's selection.
It works the first time, but the second run results in something like this:
C:\Our App\install_x\install_y, where x corresponds to the value selected with the first installation and y corresponds to the value selected during this installation.
The install version is a radio button selection grabbed from the first screen in the installer. How do I eliminate this issue?
Setup section:
[Setup]
AppName=Our App
AppId=Our App
AppVerName=Our App Version(CM)
DefaultDirName=C:\Our App
DefaultGroupName=Our Group Name
OutputDir=..\
OutputBaseFilename=mm_setup
DisableStartupPrompt=yes
Compression=zip
UsePreviousAppDir=no
VersionInfoDescription=Our App Setup
CreateUninstallRegKey=no
DirExistsWarning=no
And the method where I set the install version:
procedure gSetVersion;
begin
if gVersionPage.SelectedValueIndex = 0 then
begin
gInstallArea := 'install_a';
end
else if gVersionPage.SelectedValueIndex = 1 then
begin
gInstallArea := 'install_b';
end
else if gVersionPage.SelectedValueIndex = 2 then
begin
gInstallArea := 'install_c';
end
WizardForm.DirEdit.Text := WizardDirValue + '\' + gInstallArea;
end;
Solved the problem via the following hack. Not sure why it was necessary to manually edit the string when the `UsePreviousAppDir=no' was set, but this works
procedure gSetVersion;
var
installVersionIndex: Integer;
installDir: String;
begin
case gVersionPage.SelectedValueIndex of
0: gInstallArea := 'install_a';
1: gInstallArea := 'install_b';
2: gInstallArea := 'install_c';
end
//Set the default installation folder.
//This is necessary because InnoSetup intermittently
//ignores the 'UsePreviousAppDir=no' [Setup] directive
//and because the 'DefaultDirName' directive gets populated
//prior to the user selecting the install version
installVersionIndex := Pos('install_', WizardDirValue);
installDir := WizardDirValue;
if installVersionIndex > 0 then
begin
Delete(installDir, installVersionIndex, 20);
end
WizardForm.DirEdit.Text := installDir + '\' + gInstallArea;
end;

How to set automaticly DefaultDirName for previous Inno Setup installation?

my previous installation (A) in Inno Setup has AppID={{8ADA0E54-F327-4717-85A9-9DE3F8A6D100}.
I have another installation (B) with different AppID and I want to install it into the same directory as installation (A).
How do I get automaticly DefaultDirName? I don't want to use the same AppID, because when I uninstall the installation (B) and installation (A) stays installed, it will delete AppID string from registry (installation (A) string).
Can you help me, please?
You'll probably need some code to do what you want. You'll also need a way to find the installation directory of Application A. Here's some code that I've used
[Setup]
DefaultDirName={code:GetDefaultDir}
[Code]
function GetDefaultDir(def: string): string;
var
sTemp : string;
begin
//Set a defualt value so that the install doesn't fail.
sTemp := ExpandConstant('{pf}') + '\MyCompany\MyAppA';
//We need to get the current install directory.
if RegQueryStringValue(HKEY_LOCAL_MACHINE, 'Software\MyCompany\Products\MyAppNameA',
'InstallDir', sTemp) then
begin
//We found the value in the registry so we'll use that. Otherwise we use the default
end;
Result := sTemp;
end;
I developed the following code to find the installation directory based on AppID. It accommodates per-user registry entries as well as those for the entire machine. It has been tested on Windows 7 Enterprise on a domain and in a Virtual PC XP Professional machine:
[code]
const
PreviousAppID = '8ADA0E54-F327-4717-85A9-9DE3F8A6D100';
AppFolder = 'SomeFolder';
UninstallPath = 'Software\Microsoft\Windows\CurrentVersion\Uninstall\{'
+ PreviousAppID + '}_is1';
// Some posts have 'InstallDir', but I have never observed that
InstallKey = 'InstallLocation';
function GetDefaultDir( Param: String ) : String;
var
UserSIDs: TArrayOfString;
I: Integer;
begin
// Check if the current user installed it
if RegQueryStringValue( HKEY_CURRENT_USER, UninstallPath,
InstallKey, Result ) then
// Current user didn't install it. Did someone else?
else if RegGetSubkeyNames( HKEY_USERS, '', UserSIDs ) then begin
for I := 0 to GetArrayLength( UserSIDs ) - 1 do begin
if RegQueryStringValue( HKEY_USERS, UserSIDs[I] + '\' + UninstallPath,
InstallKey, Result ) then break;
end;
end;
// Not installed per-user
if Result = '' then begin
// What about installed for the machine?
if RegQueryStringValue( HKEY_LOCAL_MACHINE, UninstallPath,
InstallKey, Result ) then
// Doesn't appear to be installed, as admin default to Program Files
else if IsAdminLoggedOn() then begin
Result := ExpandConstant('{pf}\') + AppFolder;
// As non-admin, default to Local Application Data
end else begin
Result := ExpandConstant('{localappdata}\') + AppFolder;
end;
end;
end;

Resources