running InstallUtil {app}/file.exe in inno setup - installation

I want to copy service files to {app} directory and then use this as a parameter in InstallUtil.exe.
Here's some part of my code :
[Files]
Source: WCFService.exe; DestDir: {app}
Source: WCFService.exe.config; DestDir: {app}
[Run]
Filename: {dotnet40}\InstallUtil.exe; Parameters: {app}\WCFService.exe
This code doesn't work (but the files are copied into {app} directory correctly). However, if I use something like this :
[Files]
Source: WCFService.exe; DestDir: {src}
Source: WCFService.exe.config; DestDir: {src}
[Run]
Filename: {dotnet40}\InstallUtil.exe; Parameters: WCFService.exe
it works correctly. Does anyone know what's going on?
I have to use inno setup.

In this case you could try to set WorkingDir parameter to {app} in the [Run] section.
Like this:
[Run]
Filename: "{dotnet40}\InstallUtil.exe"; WorkingDir: "{app}"; Parameters: "WCFService.exe"

{app} may contain spaces, and so must be properly quoted when using it on command lines:
[Run]
Filename: {dotnet40}\InstallUtil.exe; Parameters: """{app}\WCFService.exe"""
The outermost set of quotes is for Inno itself; each pair of doubled quotes within that will end up putting a single quote on the command line.

Related

How to handle path with spaces in Inno Setup?

I want to allow paths with spaces (for example program files) when installing my program with Inno Setup. However paths with spaces let my installed service crash.
The Inno Setup file looks like this:
[Setup]
AppName=Demo
DefaultDirName={pf}\demo
[Files]
Source: "bin\nssm.exe"; DestDir: "{app}"
Source: "bin\jdk1.8.0_152\jre\*"; DestDir: "{app}\jre"; Flags: recursesubdirs
Source: "build\libs\demo.jar"; DestDir: "{app}"
[Run]
Filename: "{app}\nssm.exe"; \
Parameters: "install demo ""{app}\jre\bin\java.exe"" -jar ""{app}\demo.jar"""
Filename: "{app}\nssm.exe"; Parameters: "start demo"
"nssm.exe" is a service wrapper to execute a java application as a windows service.
The critical part is this line:
Filename: "{app}\nssm.exe"; \
Parameters: "install demo ""{app}\jre\bin\java.exe"" -jar ""{app}\demo.jar"""
As suggested in this question/answer, I tried to use double double quotes, but this doesn't help, the service is still crashing. If I change DefaultDirName to a path without spaces everything works as expected.
DefaultDirName=c:\demo
How do I have to handle paths with spaces?
The problem was the combination of Inno Setup and nssm, which both are escaping double quotes with double quotes. That makes multiple double quotes necessary.
Solution:
Filename: "{app}\nssm.exe"; Parameters: "install demo ""{app}\jre\bin\java.exe"" -jar """"""{app}\demo.jar"""""""
See nssm documentation section "Quoting issues".

Create a hardlink with Inno Setup

I have thousand of own installers that requires a critical dll file for uninstallation step, this dll file sizes about 2 mb then to avoid unnecessary disk space (2mb*100 installers) I would like to store the file once in {cf} then make a hardlink for the next installers that requires that file.
I could create a hardlink in Inno Setup without the need of external apps such as mklink.exe usage?
This is a brief example of what I have, all my installers follow the same "structure":
[Files]
; VCL Styles
Source: {tmp}\uninstall.vsf; DestDir: {app}; \
Flags: ignoreversion
Source: {tmp}\uninstall.dll; DestDir: {app}; \
Flags: ignoreversion uninsneveruninstall
; Temp files
Source: {tmp}\*; DestDir: {tmp}; Excludes: uninstall.dll, uninstall.vsf; \
Flags: recursesubdirs createallsubdirs ignoreversion
; Program
Source: {app}\*; DestDir: {app}; \
Flags: recursesubdirs createallsubdirs ignoreversion
As you could see, I'm moving the uninstall.dll to {app}, but what I would like to do is: If doesn't exists, copy the uninstall.dll file to {cf}\InnoSetup\uninstall.dll filepath and make a hardlink to {app}\uninstall.dll, if already exists the file then just make the hardlink, nothing more, I won't still store the uninstall.dll file in {app}\uninstall.dll, just I want a symbolic reference because the uninstall.dll file should never be uninstalled.
How I could do it?
Inno Setup does not support creating hardlinks natively.
I wouldn't consider the mklink an external application. It's a built-in Windows tool. So if you do not need to support Windows XP, you can safely rely on it. Or you can fallback to installing the DLL regularly, if the mklink is not available.
Or use the CreateHardLink function from the Code section.
#define MyApp "MyApp"
#define UninstallDll "uninstall.dll"
[Files]
Source: "{#UninstallDll}"; DestDir: "{cf}\{#MyApp}"; \
Flags: ignoreversion uninsneveruninstall
[Code]
function CreateHardLink(lpFileName, lpExistingFileName: string;
lpSecurityAttributes: Integer): Boolean;
external 'CreateHardLinkW#kernel32.dll stdcall';
procedure CurStepChanged(CurStep: TSetupStep);
var
ExistingFile, NewFile: string;
begin
if CurStep = ssPostInstall then
begin
ExistingFile := ExpandConstant('{cf}\{#MyApp}\{#UninstallDll}');
NewFile := ExpandConstant('{app}\{#UninstallDll}');
if CreateHardLink(NewFile, ExistingFile, 0) then
begin
Log('Hardlink created');
end
else
if FileCopy(ExistingFile, NewFile, False) then
begin
// FAT file system?
Log('Hardlink could not be created, file copied instead');
end
else
begin
MsgBox('Cannot install {#UninstallDll}', mbError, MB_OK);
end;
end;
end;
(Tested on Unicode version of Inno Setup – The only version as of Inno Setup 6)
And do not forget to delete the file when uninstalling:
procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
begin
if CurUninstallStep = usUninstall then
begin
if DeleteFile(ExpandConstant('{app}\{#UninstallDll}')) then
begin
Log('File deleted');
end
else
begin
Log('Cannot delete file');
end;
end;
end;
You can of course use also the [UninstallDelete] entry. I just like to uninstall the file using the same technology used to install it.
Your question title is "Create a hardlink with Inno Setup".
The CreateHardLink creates a hardlink. A hardlink is another reference to the same contents. Basically the hardlink is indistinguishable from the original file (even the original file is a hardlink actually). Both original file and the hardlink are just references to the same contents. If you delete the original file (or the new hardlink), you actually remove just one reference to the contents. The contents is still preserved. The contents is removed with the last reference only. The hardlink does not occupy an additional space on the disk (the contents is stored only once).
For details see Hard link article on Wikipedia.
While the mklink creates a symlink (aka symbolic link) by default. A symlink is like a shortcut, it's a reference to the original file (not contents). It's a file on its own, that contains a path to the target file. The symlink has a size of its own (occupied by the reference to the target file). If you remove the original file, the symlink still exists (because there's no reference to the symlink from the original file), but becomes invalid (the contents is gone). Again, it's similar to a shortcut.
For details see Symbolic link article on Wikipedia.
You can create a hardlink with the mklink, if you add the /H switch:
/H Creates a hard link instead of a symbolic link.
If you want to create the symlink instead of the hardlink, it's a different question (though the answer is simple, use the CreateSymbolicLink function). Though again, note that the hardlink does not occupy additional space on the disk, what seems to be your concern. So I believe you should keep using the CreateHardLink function.

Error with the shortcut of vb script created by inno setup

EDIT: I have made little edit to question, describing cause of problem at last.
I have build a setup using Inno. The main file, from where execution starts, is a vbs file. I have set Inno to make shortcut in desktop with a custom icon. But after installation the shortcut gives vbs error of file missing. If i go to main vbs file and run directly or create another shortcut of that vbs file manually in desktop, I can run that shortcut any number of times. So where is the problem. Is it Inno's problem or some scripting problem.
Here's the vbs script (its aim is to start a batch file but dont show cmd window while opening the batch command)
Set WshShell = CreateObject("WScript.Shell")
WshShell.Run chr(34) & "run.bat" & Chr(34), 0
Set WshShell = Nothing
I don't know vbs and this script was lying in the Internet. So if there's some obvious problem with the script, please help me correct it.
The exact error I get is:
Script: C:\Admin\start.vbs
Line: 2
Char: 1
Error: The system cannot find the file specified.
Code: 80070002
Source: (null)
Here's the script I used in Inno
; Script generated by the Inno Setup Script Wizard.
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
[Setup]
; NOTE: The value of AppId uniquely identifies this application.
; Do not use the same AppId value in installers for other applications.
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
AppName=Test
AppVersion=1.0
AppVerName=Test 1.0
AppPublisher=USV
DefaultDirName=C:\Test
DisableDirPage=yes
DefaultGroupName=Test
DisableProgramGroupPage=yes
OutputDir=C:\Users\Ashu\Desktop
OutputBaseFilename=Test
SetupIconFile=C:\Test\logo2.ico
Compression=lzma
SolidCompression=yes
; "ArchitecturesAllowed=x64" specifies that Setup cannot run on
; anything but x64.
ArchitecturesAllowed=x64
; "ArchitecturesInstallIn64BitMode=x64" requests that the install be
; done in "64-bit mode" on x64, meaning it should use the native
; 64-bit Program Files directory and the 64-bit view of the registry.
ArchitecturesInstallIn64BitMode=x64
[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"
[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
[Files]
Source: "C:\Test\start.vbs"; DestDir: "{app}"; Flags: ignoreversion
Source: "C:\Test\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
[Icons]
Name: "{group}\Test"; Filename: "{app}\start.vbs"; IconFilename: {app}\logo2.ico;
Name: "{group}\{cm:UninstallProgram,Test}"; Filename: "{uninstallexe}"; IconFilename: {app}\logo2.ico;
Name: "{commondesktop}\Test"; Filename: "{app}\start.vbs"; Tasks: desktopicon; IconFilename: {app}\logo2.ico;
[Run]
Filename: "{app}\importstarter.bat";
Filename: "{app}\start.vbs"; Description: "{cm:LaunchProgram,Test}"; Flags: shellexec postinstall skipifsilent
EDIT:
I have updated script that closely resembles the problem. Cause for the problem is in shortcut property, target property is set but start in property is set blank. Correcting it to desired directory solves the problem. Program runs in first attempt because setup directly runs from the main file instead of the shortcut. So this must be a problem of Inno Script.
Your problem is caused by a lack of specific working/current paths and the use of relative paths.
When the vbs file is run, the working/current directory would be that of the installer, c:\Windows\, j:\MyData\, etc. When it tries to run run.bat, it can't find it in the current directory, resulting in the "The system cannot find the file specified." error from the script engine.
The best way to fix this is to force the current directory of the running script to the folder containing it.
(I don't know how to do this off hand)
Alternatively, you can set the current directory on the shortcuts and the [Run] entry:
[Icons]
Name: "{group}\Test"; Filename: "{app}\start.vbs"; IconFilename: {app}\logo2.ico; WorkingDir: "{app}";
Name: "{group}\{cm:UninstallProgram,Test}"; Filename: "{uninstallexe}"; IconFilename: {app}\logo2.ico;
Name: "{commondesktop}\Test"; Filename: "{app}\start.vbs"; Tasks: desktopicon; IconFilename: {app}\logo2.ico; WorkingDir: "{app}";
[Run]
Filename: "{app}\importstarter.bat"; WorkingDir: "{app}";
Filename: "{app}\start.vbs"; WorkingDir: "{app}"; Description: "{cm:LaunchProgram,Test}"; Flags: shellexec postinstall skipifsilent
You can also put this at the top of the batch files so they don't need a specific current directory:
cd /d %~dp0
Note that recent (since 2010) versions of Inno Setup force a working directory to be set to protect against this error.
You need to specify full path and lose the last pointless line.
Set WshShell = CreateObject("WScript.Shell")
WshShell.Run chr(34) & "c:\somefolder\run.bat" & Chr(34), 0
Thanks everyone for your concern. The issue was with Inno Setup. I updated it to newer version and the problem was gone without making any changes to the script.
The problem was caused since the shortcut does not contain any starting point folder (thanks #Noodles). So it was unable to find file in the default directory of cmd.

How to create a hotkey for desktop icon using Inno Setup

How do I create a shortcut hotkey for an icon in Inno Setup?
The website says add this:
HotKey: ctrl + alt + k
Does anyone have sample code?
The HotKey is an optional parameter for [Icons] section entries, so simply add it to your icon entry e.g. this way:
[Icons]
Name: "{group}\My Program"; Filename: "{app}\MYPROG.EXE"; WorkingDir: "{app}"; \
HotKey: "ctrl+alt+k"

Inno Setup if and language

I wanted to know how to control the language of inno setup, I would like that when the user selects the English inno setup after installation eliminates the Italian language files, but if the user selects the Italian language, I would like that after the installation would remove the language file English. I tried this code but does not work:
[InstallDelete]
#if {language} = "english"
Type: files; Name: "{commondesktop}\english.txt"
#if {language} = "italian"
Type: files; Name: "{commondesktop}\italian.txt"
#endif
Thanks.
Sorry for my English.
Directives are evaluated at compile time, and in any case [InstallDelete] section is processed at the beginning of the setup. The easiest approach for your case, I believe, is to not install the file in the first place if the user has not chosen the corresponding setup language:
[Languages]
Name: "en"; MessagesFile: "compiler:Default.isl"
Name: "it"; MessagesFile: "compiler:Languages\Italian.isl"
[Files]
Source: "english.txt"; DestDir: "{commondesktop}"; Languages: en;
Source: "italian.txt"; DestDir: "{commondesktop}"; Languages: it;
If I have somehow misunderstood the question, you can use the DeleteFile support function in code to delete a file, for instance in a CurStepChanged procedure while CurStep is 'ssDone' or 'ssPostInstall'.

Resources