inno-setup update file in all subdirectories where it exists - inno-setup

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.

Related

Inno Setup Call AfterInstall for each external File

I am really struggling with this one. I have an entry in the files section of an Inno Setup (5.5.9) configuration file, that is something like the following:
[Code]
procedure showMsgBoxOfFile;
begin
MsgBox(ExpandConstant(CurrentFilename), mbInformation, MB_OK);
end;
[Files]
Source: {src}\Some\Path\myFile*Pattern.ext; DestDir: {app}; Flags: external; \
AfterInstall: showMsgBoxOfFile;
When I run the installer generated by running the above script, I get a single message box with the {app} directory, even though four files are copied. This seems to be in direct contradiction of the Inno Setup documentation on BeforeInstall/AfterInstall, which states:
A BeforeInstall or AfterInstall function for a [Files] section entry using a wildcard is called once per file matching the wildcard. Use CurrentFileName to check for which file the function is called.
Further, another question on Stack Overflow is from a user who wanted to do the exact opposite (get only one notification when multiple files were changed). The accepted answer to that question indicates that:
there is no way to call [AfterInstall] once after all the files are installed
I noticed that if I remove the "external" flag from my file copy, I DO get one message box per file. Is there a way to get a one notification per copied file when the "external" flag is specified? Does anyone know if there is a plan to document this difference in the Inno Setup help files?
Indeed, for entries with the external flag, the BeforeInstall and AnswerInstall functions are called only once.
What you can do, is to copy the files programmatically. It would give you a full control over, what you do for each file.
See Inno Setup: copy folder, subfolders and files recursively in Code section

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.

Get the path specified in the source of file section

Is there any way by which we can get the path of the source file in [file] section be made available in [code] section. I need to have the full path as been given in the source. I need to check it with the content of a file and if the path is present in the file, then only i need to copy that particular file. I am using Check: in the file section and need to have the whole path of file made available in code section for comparison.
To get the chosen install folder from pascal script, you can use either ExpandConstant('{app}') or WizardDirValue(). Note that I don't think the returned path contains a trailing backslash.
This would simply check a file existence:
function IsMyFilePresent: Boolean;
begin
Result:=FileExists(ExpandConstant('{app}\filename.ext'));
end;
If it's an ini file, you can use this code to retrieve the data of certain keys inside it:
(example using WizardDirValue())
inifile:=WizardDirValue()+'\filename.ext';
MyString:=GetIniString('SectionName', 'KeyName', 'DefaultValue', inifile);
Probably the CurrentFileName() function that:
Returns the destination name of the [Files] entry that is currently being processed.
You can probably work out the source from this. I'm not sure how it handles wildcards though (but I suspect it just returns "blah/*"

Write to AppData directory using InnoIDE?

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

Resources