Inno setup to add an entry in hosts file under windows 7 - inno-setup

Can anyone help me with a inno setup sample script showing how to add an entry to windows 7 hosts file?
Thanks

lmhost supports #include statements, so you can include your own hosts file like this:
//call after inno setup step change
procedure UpdateLMhosts(CurStep: TSetupStep);
var
contents: TStringList;
filename, statement: String;
i: Integer;
begin
if(CurStep=ssDone) then begin
filename := ExpandConstant('{sys}\drivers\etc\lmhosts');
Log('Reading ' + filename);
contents := TStringList.Create();
if(FileExists(filename)) then begin
contents.LoadFromFile(filename);
end;
//copy my lmhosts to the system's lmhosts
statement := ExpandConstant('#INCLUDE {commonappdata}\MyBrand\MyApp\lmhosts');
if(contents.IndexOf(statement) < 0) then begin
Log('Adding' + statement);
contents.Append(statement);
contents.SaveToFile(filename);
end;
end;
end;

This seems like a task outside of the scope of what Inno Setup provides.
See the following Knowledge Base article for suggestions: http://www.jrsoftware.org/iskb.php?custom

Related

Deleting all files with some exceptions after installation with Inno Setup

How can I delete a series of files once the Run has been executed?
I want to delete a lot of DLL files and some other files because they are now in separate sub folders. But some of the DLL files are registered COM etc. Once Run has finished the old DLLs will not be registered anymore. So it will be safe for me to delete them.
InstallDelete is too early:
https://jrsoftware.org/ishelp/index.php?topic=installorder
So I want ability to delete *.dll but exclude pattern*.dll ONCE install finished.
I've seen a similar question:
Delete a file AFTER installation in Inno Setup
It seems I need to use something like:
[Code]
procedure CurStepChanged(CurStep: TSetupStep);
begin
if CurStep = ssPostInstall then begin
DeleteFile(ExpandConstant('path'));
..
Ideally I want to delete:
{app}\*.dll
{app}\*.config
{app}\GoogleAuthAndSync.exe
But, I want to KEEP these files in the {app} folder:
{app}\MeetSchedAssist*.dll
{app}\VclStylesinno.dll
That is what I want to achieve. This is because the files are now being installed into distinct sub-folders and being managed there, and not all mixed up in the app folder.
The CurStepChanged(ssPostInstall) seems like the the right approach to me. So all you need on top of that is a way to delete all files, with some exceptions, right?
Start here:
Delete whole application folder except for "data" subdirectory in Inno Setup
If you need to know how to test for partial filename (MeetSchedAssist*.dll):
If you need just one-off test, an ad-hoc condition like this will do:
(CompareText(Copy(FindRec.Name, 1, 15), 'MeetSchedAssist') = 0) and
(CompareText(Copy(FindRec.Name, Length(FindRec.Name) - 3, 4), '.dll') = 0)
If you have many tests, you can use MatchesMaskEx function from Inno Setup - Integer or Set/Range wildcard?:
MatchesMaskEx('MeetSchedAssist*.dll', FindRec.Name)
I thought I would add my final code:
(* Cannot use built-in DeleteFile directly in AfterInstall as it's a function,
not a procedure. And this way we can add some error handling too. *)
procedure DoDeleteFile(FileName: string);
begin
if (FileExists(FileName)) then
begin
if DeleteFile(FileName) then
begin
Log(Format('"%s" deleted', [FileName]));
end
else begin
MsgBox(Format('Failed to delete "%s"', [FileName]), mbError, MB_OK);
end;
end;
end;
{ Deletes obsolete DLL files etc. }
procedure DelObsoleteFiles(Path: string);
var
FindRec: TFindRec;
FilePath: string;
begin
if FindFirst(Path + '\*.dll', FindRec) then
begin
try
repeat
FilePath := Path + '\' + FindRec.Name;
if CompareText(FindRec.Name, 'VclStylesinno.dll') = 0 then
begin
Log(Format('Keeping DLL %s', [FilePath]));
end else if CompareText(Copy(FindRec.Name, 1, 15), 'MeetSchedAssist') = 0 then
begin
Log(Format('Keeping DLL %s', [FilePath]));
end else begin
DoDeleteFile(FilePath);
end;
until not FindNext(FindRec);
finally
FindClose(FindRec);
end;
end else
begin
Log(Format('Failed to list %s', [Path]));
end;
DoDeleteFile(Path + '\GoogleAuthAndSync.exe')
DoDeleteFile(Path + '\GoogleAuthAndSync.exe.config')
end;
procedure CurStepChanged(CurStep: TSetupStep);
var
ResultCode: integer;
begin
if (CurStep = ssPostInstall) then
begin
(* Delete obsolete DLL files etc. because they are now maintained in
separate sub-folders *)
DelObsoleteFiles(ExpandConstant('{app}'));
end;
end;

Displaying custom status message while extracting archive in Inno Setup

I have an installation file for my app and it contains the .exe file and a .zip file as well.
What I want is:
Recently I added a code in the [Code] section that is extracting files from zip, But actually, it happened after the installation is done, and the progress bar is 100%, So what I want is to make that code's (unzipping) process work with the progress bar and show the user what is extracting at the moment.
For example: let's say extracting files will take 50% of the progress bar and the rest will take it the code section while it is unzipping, with the state about what is extracting at the moment.
Here is the code:
[Code]
procedure InitializeWizard;
begin
ForceDirectories(ExpandConstant('{localappdata}\folder-A\app\folder-B'))
end;
const
SHCONTCH_NOPROGRESSBOX = 4;
SHCONTCH_RESPONDYESTOALL = 16;
procedure unzip(ZipFile, TargetFldr: variant);
var
shellobj: variant;
SrcFldr, DestFldr: variant;
shellfldritems: variant;
begin
if FileExists(ZipFile) then begin
if not DirExists(TargetFldr) then
if not ForceDirectories(TargetFldr) then begin
MsgBox('Can not create folder '+TargetFldr+' !!', mbError, MB_OK);
Exit;
end;
shellobj := CreateOleObject('Shell.Application');
SrcFldr := shellobj.NameSpace(ZipFile);
DestFldr := shellobj.NameSpace(TargetFldr);
shellfldritems := SrcFldr.Items;
DestFldr.CopyHere(
shellfldritems, SHCONTCH_NOPROGRESSBOX or SHCONTCH_RESPONDYESTOALL);
end;
procedure CurStepChanged(CurStep: TSetupStep);
begin
if CurStep = ssPostInstall then
begin
unzip(ExpandConstant('{app}\example.zip'),ExpandConstant('{app}\'));
end;
end;
The easiest solution is to use the WizardForm.StatusLabel:
WizardForm.StatusLabel.Caption := 'Extracting...';
For more fancy solution you can use TOutputProgressWizardPage. For an example that even displays a progress bar (though by using a DLL for the extraction), see:
Inno Setup - How to add cancel button to decompressing page?
This post lists more related examples:
Inno Setup: How to modify long running script so it will not freeze GUI?

Delete files/shortcuts matching a file mask on uninstall

I have created a shortcut named Myapp in the desktop. My installed app changes that shortcut, if I choose to others languages, for example: Spanish or French. Then the shorcut name changes to: Myapp Spanish or Myapp French.
That s why Inno Setup can not detect it on uninstall. And this doesn't work wither :
[UninstallDelete]
Type: files; Name: "{commondesktop}\Myapp*.ink";`
To delete files matching a mask on uninstall, you can use:
[Code]
function DeleteWithMask(Path, Mask: string): Boolean;
var
FindRec: TFindRec;
FilePath: string;
begin
Result := FindFirst(Path + '\' + Mask, FindRec);
if not Result then
begin
Log(Format('"%s" not found', [Path + '\' + Mask]));
end
else
begin
try
repeat
FilePath := Path + '\' + FindRec.Name;
if not DeleteFile(FilePath) then
begin
Log(Format('Error deleting "%s"', [FilePath]));
end
else
begin
Log(Format('Deleted "%s"', [FilePath]));
end;
until not FindNext(FindRec);
finally
FindClose(FindRec);
end;
end;
end;
procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
begin
if CurUninstallStep = usUninstall then
begin
Log('Deleting shortcuts')
DeleteWithMask(ExpandConstant('{commondesktop}'), 'Myapp*.ink');
end;
end;
(I'm not sure, what .ink is about, though)
Safer would be to iterate all shortcut files in the folder (desktop), deleting only those that point to your application.
See my answer to Check for existence of a shortcut pointing to a specific target in Inno Setup.
If I understand your question correctly, your application can already identify the correct shortcut file (as it seems to rename or delete the old shortcut, when the language changes). In that case, consider adding the "uninstall shortcut" functions to the application itself. Make the application process (undocumented) command-line switch to delete the shortcut (e.g. /DeleteShortcut). And use that from [UninstallRun] section:
[UninstallRun]
Filename: "{app}\MyApp.exe"; Parameters: "/DeleteShortcut"; RunOnceId: "DeleteShortcut"

How to display localized Program Files name (display name) during installation?

I'm currently creating an installer, which has Program Files as its default installation directory. To do this, I used {pf}.
It's a German program and only used in Germany and while the installer is entirely in German during selection of the destination directory, setup still displays C:\Program Files instead of the localized name C:\Programme.
Is it possible to get it to display C:\Programme instead? Functionally everything works fine, the application is installed in C:\Programme. I'm just concerned a basic user may be confused by reading C:\Program Files.
EDIT: Further information: I know C:\Programme or any other localized name for Program Files is just a display name, the physical path is always Program Files. Doesn't matter which Windows version or what language Windows has. Yet I'd still like setup to display C:\Programme during installation.
My test machines are on Windows 7 and Windows 10.
Inno Setup does not support that.
You would have to fake it. You can dynamically translate contents of the DirEdit to/from a display name as needed:
translate to display name, when the "Select Destination Location" page is activated
translate to physical path, when "Browse" button is clicked.
translate to display name, when new path is selected.
translate to physical path, when "Next" button is clicked.
function ToDisplayName(Path: string): string;
begin
Result := ???;
end;
function FromDisplayName(Path: string): string;
begin
Result := ???;
end;
var
DirBrowseButtonClickOrig: TNotifyEvent;
OnSelectDir: Boolean;
procedure DirBrowseButtonClick(Sender: TObject);
begin
WizardForm.DirEdit.Text := FromDisplayName(WizardForm.DirEdit.Text);
DirBrowseButtonClickOrig(Sender);
WizardForm.DirEdit.Text := ToDisplayName(WizardForm.DirEdit.Text);
end;
procedure InitializeWizard();
begin
DirBrowseButtonClickOrig := WizardForm.DirBrowseButton.OnClick;
WizardForm.DirBrowseButton.OnClick := #DirBrowseButtonClick;
OnSelectDir := False;
end;
procedure CurPageChanged(CurPageID: Integer);
begin
if CurPageID = wpSelectDir then
begin
OnSelectDir := True;
WizardForm.DirEdit.Text := ToDisplayName(WizardForm.DirEdit.Text);
end
else
begin
if OnSelectDir then
begin
OnSelectDir := False;
WizardForm.DirEdit.Text := FromDisplayName(WizardForm.DirEdit.Text);
end;
end;
end;
A tricky part is of course the implementation of the ToDisplayName and FromDisplayName functions.
A real native implementation would be pretty complex and it's even questionable if you can implement it with limited features of the Pascal Script (particularly a lack of pointers).
But for your specific needs, you can use something as trivial as:
[CustomMessages]
ProgramFilesLocalized=Programme
[Code]
function ToDisplayName(Path: string): string;
begin
StringChange(Path, '\Program Files', '\' + CustomMessage('ProgramFilesLocalized'));
Result := Path;
end;
function FromDisplayName(Path: string): string;
begin
StringChange(Path, '\' + CustomMessage('ProgramFilesLocalized'), '\Program Files');
Result := Path;
end;
If you need a real implementation for converting to/from display name, consider asking a separate question.

How to force the first char of the directory edit box upper case?

While running Inno Setup, I have the need of making sure the drive letter of the install directory is uppercase after being entered by a user.
The reason: Apache's mod_xsendfile requires it. It doesn't work (in httpd.conf) if the drive letter is entered in lower case. And only the drive letter must be changed; Apache can't handle it either if any characters of a path don't match the case of the path in the file system (despite Windows being case-insensitive).
How do I make the Inno Setup {app} constant automatically capitalize its first character?
This piece of code should keep the first letter of the directory edit box always upper case:
[Code]
procedure DirEditChange(Sender: TObject);
var
S: string;
SelStart: Integer;
SelLength: Integer;
begin
S := WizardForm.DirEdit.Text;
if Length(S) > 0 then
begin
SelStart := WizardForm.DirEdit.SelStart;
SelLength := WizardForm.DirEdit.SelLength;
Insert(UpperCase(S[1]), S, 2);
Delete(S, 1, 1);
WizardForm.DirEdit.Text := S;
WizardForm.DirEdit.SelStart := SelStart;
WizardForm.DirEdit.SelLength := SelLength;
end;
end;
procedure InitializeWizard;
begin
WizardForm.DirEdit.OnChange := #DirEditChange;
end;

Resources