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"
Related
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;
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 traverse a directory and its sub directories in Inno Setup Pascal scripting? I can not find any method and interface in Inno Setup Help Document.
Use FindFirst and FindNext support functions.
procedure RecurseDirectory(Path: string);
var
FindRec: TFindRec;
FilePath: string;
begin
if FindFirst(Path + '\*', FindRec) then
begin
try
repeat
if (FindRec.Name <> '.') and (FindRec.Name <> '..') then
begin
FilePath := Path + '\' + FindRec.Name;
if FindRec.Attributes and FILE_ATTRIBUTE_DIRECTORY = 0 then
begin
Log(Format('File %s', [FilePath]));
end
else
begin
Log(Format('Directory %s', [FilePath]));
RecurseDirectory(FilePath);
end;
end;
until not FindNext(FindRec);
finally
FindClose(FindRec);
end;
end
else
begin
Log(Format('Failed to list %s', [Path]));
end;
end;
For examples of use, see:
Inno Setup: copy folder, subfolders and files recursively in Code section
Search subdirectories for Inno Setup DestDir
Inno Setup: Check if file exists anywhere in C: drive
Inno Setup get directory size including subdirectories
My problem is that i would like to make an "hyperlink"(i know there is now such thing in inno) when you click label a document(rtf) with instructions will open.
The problem: i DON'T want to copy this program along with the setup,
it should be inside the setup and after the installation it is no more
needed, thus it should be deleted or thrown out.
cant use {tmp} folder since it is accesed only in [run] phase(that is installation if i am not mistaken) and i need it earlier.
Any suggestions?
The temporary folder is not explicitly reserved for [Run] section. It can be used whenever needed (it is widely used e.g. for DLL libraries). And there is no such thing as a hyperlink label in Inno Setup as far as I know. I've made an example of a link lable and extended it for opening files that are extracted from the setup archive into a temporary folder (a folder that is deleted when the installer terminates):
[Setup]
AppName=My Program
AppVersion=1.5
DefaultDirName={pf}\My Program
[Files]
; the dontcopy flag tells the installer that this file is not going to be copied to
; the target system
Source: "File.txt"; Flags: dontcopy
[Code]
var
LinkLabel: TLabel;
procedure LinkLabelClick(Sender: TObject);
var
FileName: string;
ErrorCode: Integer;
begin
FileName := ExpandConstant('{tmp}\File.txt');
// if the file was not yet extracted into the temporary folder, do it now
if not FileExists(FileName) then
ExtractTemporaryFile('File.txt');
// open the file from the temporary folder; if that fails, show the error message
if not ShellExec('', FileName, '', '', SW_SHOW, ewNoWait, ErrorCode) then
MsgBox(Format('File could not be opened. Code: %d', [ErrorCode]), mbError, MB_OK);
end;
procedure InitializeWizard;
begin
LinkLabel := TLabel.Create(WizardForm);
LinkLabel.Parent := WizardForm;
LinkLabel.Left := 8;
LinkLabel.Top := WizardForm.ClientHeight - LinkLabel.ClientHeight - 8;
LinkLabel.Cursor := crHand;
LinkLabel.Font.Color := clBlue;
LinkLabel.Font.Style := [fsUnderline];
LinkLabel.Caption := 'Click me to read something important!';
LinkLabel.OnClick := #LinkLabelClick;
end;
procedure CurPageChanged(CurPageID: Integer);
begin
LinkLabel.Visible := CurPageID <> wpLicense;
end;
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