Write to AppData directory using InnoIDE? - inno-setup

I need to write to the:
C:\Users\user\AppData\Roaming\AppName
folder during the setup process. I'm using the InnoIDE program for setup, and it's awesome. However, it seems to be lacking an 'AppData' special folder for easily accessing that directory.
For example, you can use:
DestDir: "{app}"
to write the application directory.
But how do you get to AppData? Is there a list of all the preset DestDir options?

Arg, got it.
{userappdata} → C:\Users\user\AppData\Roaming\AppName
as found here:
http://news.jrsoftware.org/news/innosetup/msg74694.html

As you said in your own answer, {userappdata} is the one you need. The help manual has a list of all the Inno Setup directory constants.

It is not recommend to write to AppData directory using InnoIDE. You better save user-specific data to a shared location first (e.g. under {app}) and have your application copy it from there on first startup.
more https://stackoverflow.com/a/3036259/5929293

Related

Why doesn't Inno Setup constant {userdesktop} work in OutputDir directive and how to fix it?

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

inno-setup update file in all subdirectories where it exists

I'm trying to create an installer that will update a single csv file, that exists in various locations depending on which of our applications a user has installed.
Our applications install inside a folder with our Company Name in Program Files.
So, Company > App1, Company > App2, Company > App3, etc. When the csv file exists, it is directly inside the App folder.
I've tried:
[Files]
Source: "file.csv"; DestDir: "{pf}\Company\*";
With various flags and functions to no avail
flag: onlyifdestfileexists
Skips everything because it doesn't exist
Check: FileExists('file.csv')
Does nothing at all
Check: FileExistsWildcard('file.csv')
Which calls a function using FindFirst() tries to create a new directory called * to install the file in, rather than overwriting the file in the directory where it was found, as does
[Files]
Source: "file.csv"; DestDir: "{pf}\Company\"; Check: FileExistsWildcard('*\file.csv')
The problem seems to have to do with the wildcard * not doing anything when used with DestDir. Instead of searching through all subfolders, it just looks for a subfolder named "*". The recursesubdirs flag of course works for iterating over subdirectories for Source, but there is no equivalent for DestDir.
Wildcards like that are not supported in DestDir.
If you know the names of all possible subdirectories up front (which presumably is the case as these are your own applications) then the simplest way to do this is to include multiple [Files] entries, one for each possible application. You can then use the onlyifdestfileexists flag. Note that when using the same Source path on multiple entries, only one copy of the file will be stored inside the installer, so it won't bloat the size.
If your applications support being installed into different locations (as most do) then you'll need to modify this slightly; instead of hard-coding the DestDir you'll need to have each entry look something like this:
Source: file.csv; DestDir: {code:FindAppPath|App1}; Flags: onlyifdestfileexists
with corresponding [Code] function:
function FindAppPath(AppName: String): String;
In this function, use RegQueryStringValue or other methods to look up the currently installed location of the application being searched for (via AppName, which can either be an arbitrary string of your choosing or could be part of the registry path being looked up; alternately you could write separate functions for each application if that's easier). If a given application is not installed, then return '', which will then skip installing the file due to the onlyifdestfileexists flag. Note that you will still need one entry per application, so you will need to know in advance the maximum number of possibly installed applications.
(An alternate means of skipping installation if you didn't wish to use this flag is to define a Check function eg. Check: IsAppInstalled('App1') or Check: IsApp1Installed.)
If you don't know (or don't want to rely on knowing) the maximum possible number of installed applications up front, then the only solution is to fall back to pure [Code]. You will need to have a single [Files] entry like so:
Source: file.csv; DestDir: {tmp}; AfterInstall: CopyToApplications
And then implement the CopyToApplications procedure to locate the paths to all the possible installations (again, probably via RegQueryStringValue) and then for each one:
FileCopy(ExpandConstant('{tmp}\file.csv'), AddBackslash(PathToApp) + 'file.csv');
You may also want to look at the CodeDlg example script and use CreateOutputProgressPage to provide feedback to the user while this process is going on, although it may not be necessarily if the file size is small enough.
Note that in all cases you probably want to disable uninstallation support (since each app's own individual uninstaller will take care of it); make sure you have Uninstallable=no in the [Setup] section.

Qt Installer framework - Copying files to location other than install directory

I know that, whatever data is placed in package/component dir/data, will be copied to the install directory. What I mean is if I have a binary, readme, license.txt inside package/component dir/data/myapp, package/component dir/data/readme, package/component dir/data/license.txt and if I choose my target installation dir to be “/opt/myfirstapp”, then inisde /opt/myfirstapp, I will have 3 files copied, myapp, readme, license.txt.
Having said that, I also have a “/usr” directory with in package/component dir/data/, however this is not the standard “/usr” which will be inside root “/”, it is just a replica. Now inside my replica “/usr” I have some directory hierarchy and some files, like /usr/bin/myapp, /usr/lib/libmyapp.so, /usr/share/icons” and many more, infact a lot. Now I want the replica “/usr” content to be copied to “/usr” (the original usr inside root folder). I should also make sure that I just add new contents to “/usr” (root /usr), but delete any existing content.
Question is clear, some files inside my data directory will have to go to target install dir, but some selected ones (for ex: /usr) will have to be copied to other paths. How do I achieve this.
Currently we have the same problem in my company: we need 2 target directories, one for the exe and one for the libraries (well, it's a bit more complex but in few words...).
After having spoken with Qt support and got the answer that it's actually not possible ("It is possible only after extracting. After extraction, you can use copy or move operation, unfortunately there is currently no other way.") I decided to use the AdminTargetDir as the second target directory. This because there's no other way to pass dynamic variables to the IFW. So after installation I call a "finalizeInstall_patch.bat" file passing the TargetDir and AdminTargetDir and this will move the libraries directory from TargetDir to AdminTargetDir. Why a .bat patch file ? because it's actually not possible to move a directory using the methods provided by the IFW. Qt support just opened a suggestion-ticket for our problem: https://bugreports.qt-project.org/browse/QTIFW-595
I hope that this answer will help others having same similar problems.
NOTE: There is a way to move a directory (on Windows), calling addOperation("Execute", "cmd /C move source dest...") but this brings to other problems out of topic here.
This worked for us (Qt Installer, macOS):
var args = ["cp", "-R", "#TargetDir#/MyApp.app", "/Applications"];
component.addOperation("Execute", args);

Calling consecutive DLL's from {tmp} in Inno Setup

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.

Inno-Setup checking file location prior to installation, then using it during installation

I need to check for the location of a file during program installation utilizing inno setup. I then need inno setup to use the location of that file in the "Filename" line to create a desktop ICON for program initialization. I have the code for the "Icons" option working fine with the exception of how to do the above.
Here is the line of code I am currently using;
Name: "{commondesktop}\SA - NH Bricscad V12"; Filename:"**c:\program files\septic assistant\new hampshire\support\**SA - NH Bricscad V12.exe"; IconFilename: "C:\Program Files\Septic Assistant\New Hampshire\Support\Bricscadlogo.ico"; Comment: "Septic Assistant the only Septic Design Program"
Hi-Lited section would be the path to the exe file that I need inno setup to search for.
Any assistance with this would be very much appreciated.
Bruce
Just use a {code:...} constant and corresponding [Code] function that returns the appropriate path for your [Icons] entry. You will probably also want to use a Check function to avoid installing the icon in the case that you cannot find the correct location.
Another option is to use a {reg:...} constant, assuming that the path you are trying to locate is specified somewhere in the registry (which is usually the case).
If the path is not already specified somewhere well-defined in the Registry when the other app is installed, and you don't have some other means to quickly identify where the other app is located (note that doing a global search of the user's HD is not a valid option), then you should add a page that prompts the user to enter the location themselves (which you can then verify that they have chosen the correct location). You can see examples of prompting the user for information and then doing something with that info in the CodeDlg.iss example included with Inno, and in the ISXKB wiki.

Resources