Is there a way to assign an environment variable to a variable on runtime? And then edit it?
So for instance, a clients computer has a program installed at c:\Company\CoolProgram and that folder location is in a variable called %InstallLocation%. So if you echo %InstallLocation% you will get c:\Company\CoolProgram
I want to install a plugin into c:\Company. So that CoolProgram and PlugIn will sit next to each other in c:\Company. I read that this could be done by defining a variable and assigning %installLocation% to it, then then trimming off \CoolProgram from the variable. So for example:
#define PluginLocation GetEnv('InstallLocation')
#define PluginLocation2 StringChange(PluginLocation, "\CoolProgram ", "")
So, PluginLocation2 = c:\Company, and that's good. Except this is going on during compile time. The build machines don't have this environment variable, plus not all clients are going to have the same installed location. Therefore %InstallLocation% is actually just empty, and therefore PluginLocation and PluginLocation2 are empty.
So I tried moving the variable definitions down into [Setup], hoping the environment variables would be looked up during runtime on the client. That didnt seem to work. I also tried into [Files] since I have files to move into the new folder, but [Files] appears to be during compile as well.
I think
procedure Test () ;
begin
MsgBox(ExtractFileDir(ExpandConstant( '{app}' ) ) , mbConfirmation, MB_YESNO ) ;
end;
is what You are looking for.
ExpandConstant( '{app}' ) will return location where application is installed.
And: ExtractFileDir will get c:\Company from c:\Company\CoolProgram.
Related
I created Inno Setup script, which works perfectly, but I wanted to change OutputDir to create the output file on my desktop. But instead of creating output file on desktop it was creating subfolder {userdesktop} at the same directory, where script is and output was inside.
I found solution so far, but I believe there should be better way. What am I missing?
; these attempts didn't work
[Setup]
OutputDir={userdesktop}
; some more attampts:
OutputDir=userdesktop
OutputDir=userdesktop:
OutputDir="{userdesktop}"
; this workaround worked for me
[Setup]
OutputDir=userdocs:..\Desktop
Constants like {userdesktop} are resolved on install time (on the target user's machine), not on compile time (on your development machine). So it makes no sense to use constants in compile-time only directive like OutputDir. And actually it's not possible to use them at all (as it's useless).
With the default user profile directory layout, use you can use the userdocs: prefix, as you did:
[Setup]
OutputDir=userdocs:..\Desktop
Though it's not a perfect solution, as the "Documents" folder can be moved by the user and then the userdocs:..\Desktop will not point to the desktop.
A more reliable solution is to use USERPROFILE environment variable using GetEnv preprocessor function:
[Setup]
OutputDir={#GetEnv('USERPROFILE')}\Desktop
Sometimes it happens, that some files of my application are used by some processes. For example, user opens application log, or something like that, and forgets to close it. This causes some errors while installing/upgrading/uninstalling. In such cases, I'd like to find out, what process is using file, and show user a message, indicating, that files are used.
Is it possible in Inno Setup to find out, what process prevents script from modifying file? At least, when I'm trying to do this in Code section.
The Inno Setup can automatically check, if the installed files are locked by some processes, and offer a user to close (and restart later) the applications automatically (since 5.5.0).
Make sure the CloseApplications directive is set to its default value yes.
Though by default, only *.exe,*.dll,*.chm files are checked. If you want to check also other or all other files, modify the CloseApplicationsFilter directive:
[Setup]
; default
CloseApplications=yes
; check all files
CloseApplicationsFilter=*.*
If you are installing some files by a code, use the RegisterExtraCloseApplicationsResources event function:
procedure RegisterExtraCloseApplicationsResources;
begin
RegisterExtraCloseApplicationsResource(
False, ExpandConstant('{userappdata}\My Program.log'));
end;
After creating my setup.exe I have to pack it for various software deployment tools. Therefore I can't call the setup.exe with parameters, instead I have placed my own parameters in a setup.ini file next to the setup.exe
[Code]
var
MyIniFile: String;
function InitializeSetup(): Boolean;
var
LoadFromIniFile: String;
begin
Result := true;
MyIniFile := ExpandConstant('{srcexe}'); //writes the full path of the setup.exe in "MyIniFile"
MyIniFile := Copy(MyIniFile, 1, Length(MyIniFile) - Length(ExtractFileExt(MyIniFile))) + '.ini'; //changes the ".exe" into ".ini"
if FileExists(MyIniFile) then LoadFromIniFile := MyIniFile; //checks wether there is a ini-file
if LoadFromIniFile <> '' then begin
MyLogFile := GetIniString('Setup', 'Log', MyLogFile , LoadFromIniFile);
ProductName := GetIniString('Setup', 'ProductName', ProductName, LoadFromIniFile);
end;
end;
Now I want to also place the so called "Setup Command Line Parameters" (listed on the Inno Setup Help site) in my ini-file. I think that there is a way for the /Dir="x:\dirname parameter, which I did not figure out yet. But I also want to have the /SILENT parameter in there, do you think there is a way to do this? If yes, how would you do this? If not, can you please give me a hint why not?
So customize your installer for different products, I'd recommend you to use a pre-processor and automatically build the installer for each product (with different "defines"), instead of using an external INI file.
For example to be able to change application name and resulting executable when building the installer, use a script like:
[Setup]
AppName={#AppName}
OutputBaseFilename={#BaseFilename}
Now you can create two different installers automatically using command-line:
ISCC.exe Example1.iss /dAppName=App1 /dBaseFilename=SetupApp1
ISCC.exe Example1.iss /dAppName=App2 /dBaseFilename=SetupApp2
Regarding the implicit silent installation:
There's no API other than the command-line /SILENT switch to trigger silent installation.
But you can create a near-silent installation by disabling most installer pages:
[Setup]
DisableWelcomePage=true
DisableDirPage=true
DisableProgramGroupPage=true
DisableReadyPage=true
DisableFinishedPage=true
Actually the above example disables all default pages. But Inno Setup compiler will ignore the DisableReadyPage=true, if all other previous pages are disabled.
You may want to choose a different page to show instead. For example a Welcome page (by omitting DisableWelcomePage=true, but keeping the DisableReadyPage=true).
If you do not mind about using external files (as you already use an external INI file), you can of course wrap the installer to a batch file and call the installer with the /SILENT switch.
I am trying to use the {reg} constant for DefaultDirName where it references a path that contains braces:
DefaultDirName={reg:HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{3E4A76D9-EC0E-4806-915C-8BC2B3C0011B},InstallLocation}
However, this does not work as the compiler thinks the GUID in the path is a constant. If I try and escape the brace with another brace as suggested (see below) this does not work either and gives an 'Invalid registry constant' error.
DefaultDirName={reg:HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{{3E4A76D9-EC0E-4806-915C-8BC2B3C0011B},InstallLocation}
I have tried all combinations I can think of to try and escape this and get the compiler to recognise this, including using %7d to try and force a closing brace, as suggested in the documentation as well, but this does not seem to compile to a closing brace in this situation. Hopefully someone can advise how to get the compiler to recognise this Registry location or at least tell me whether I am attempting to do something that is not possible. If so, is there another way to attempt this? Given that I have also already tried:
DefaultDirName={code:GetExistingInstallPath}
[Code]
function GetExistingInstallPath(Param: String): String;
begin
RegQueryStringValue(HKLM, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{F34A6950-7E0E-4F92-8B0E-C552F4989DA}',
'InstallLocation', strExistingInstallPath);
Result := strExistingInstallPath;
end;
which does compile, but the strExistingInstallPath returns nothing and hovering over the {code:GetExistingInstallPath} returns an 'Exception: Cannot evaluate "code" constant because of possible side effects.' After a couple of hours trying to get this working, I am getting close to concluding that Inno Setup does not support Registry locations containing braces.
Note, that I need to read this Registry key as the software was not installed by Inno Setup and this is a patch to replace a file, so I need to know where it was originally installed to.
In case of using {reg:...} instead of closing } you have to use %7d
Example:
DefaultDirName={reg:HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\
{{3E4A76D9-EC0E-4806-915C-8BC2B3C0011B%7d,InstallLocation}
When reading Registy in [Code] section if there is NO ExpandConstant used, you do not have to use double opening {{
Example:
RegQueryStringValue(HKLM, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\
{3E4A76D9-EC0E-4806-915C-8BC2B3C0011B}','InstallLocation', strExistingInstallPath);
P.S. Thanks #Tlama for pointing out my inexactness.
I want to use a DLL (lets say 'A.dll'), that I created in Delphi, within my Inno Setup script that uses a bunch of other DLLs ('B.dll', 'C.dll', ...). All these dll-files are included in the Files-section as follows:
[Files]
Source:"libs\*.dll"; Flags: dontcopy
In the code section I declare methods of the used DLL as described in the Online help and add the loadwithalteredsearchpath flag:
procedure MyMethod; external 'MyMethod#files:A.dll,B.dll,C.dll stdcall loadwithalteredsearchpath';
When the installer starts, all needed files are copied into the temporary directory the constant {#tmp} is pointing to. However, MyMethod starts its execution just fine (checked it with some Showmessages), but the whole thing breaks, when the method tries to use the other DLLs.
Next to the temporary folder from {#tmp} two other temporary directories are created during the setup (all with the 'IS-xxxxx.tmp' pattern), which contain 'setup.tmp' (which is not occurent in {#tmp}). When I now manually copy all the DLL's (besides A.dll) into both these other directories at the beginning of the setup, then everything works fine. But when I let it run only as defined in my script, then A.dll doesn't seem to find the other libraries.
Does anybody know, why this is happening and how I can fix this? This seems to be a problem with the PATH, but I thought that Inno Setup adds the tmp-dir into the PATH, so that the setup can find the DLL's (which it does, but strangely only for A.dll).
Thanks in advance for your help! :)
EDIT: The actual error I get, when I use one of the 'foreign' DLL's (B.dll, C.dll, ...) by calling one of their methods inside of A.dll during the Inno Setup:
Access violation at address 00408CC7 in module 'setup.tmp'. Read of adress 00000000.
EDIT 2: I think I realized why my problem is happening: With ExtractFilePath (first link) in my own A.dll I discovered, that the setup.exe is not executed within {tmp} but one of the other two temporary dirs that are creating at the beginning of the setup. It also appears, that not {tmp} but the current working dir (thus the dir, where inno is executed) is added to the library search path (second link). This would explain, why the other libraries (B.dll, C.dll, ...) can only be accessed when manually copying to this other temp dir. I suppose that A.dll is extracted and called from {tmp} without a problem, because it is referred as the "main-library" in the external command. I thought that with loadwithalteredsearchpath the other libraries could remain in the same directory, but that doesn't seem to work.
But how can I fix this now in an nifty way? I think I could copy the DLLs manually to the setup-path (by using ExtractFilePath(ParamStr(0)), after they have been extracted to {tmp} to solve the problem. But this seems to be a dirty workaround as using DLLs in Inno Setup is supposed to work differently.
How to get path where is temporary Inno setup file is located
External function calls with multiple dependent DLLs
Well I'm not sure if you only load the DLLs without registering them in the system registry. However your first EDIT shows an error triggered by attempts to access some stack of the registry, so I assume you are. In that case, I simply use a batch file (which fires commands in the CMD console) to register my DLLs as I would one by one:
#echo off
echo Registering DevExpress DLLs
%~dp0gacutil.exe /i %~dp0DevExpress.BonusSkins.v12.1.dll
%~dp0gacutil.exe /i %~dp0DevExpress.Charts.v12.1.Core.dll
So, I place this in the RUN section of the iss script:
[Run]
Filename:C:\myFolder\RegisterDevExpress.bat"
Hope this helps.