Is there a way to set a property value to the formatted install date/time - wix

Is there a way to set a property value to the formatted install date/time?
I'm in the process of creating an MSI installer for an old VB6 application we still depend on (yes, I know, upgrade before it dies).
I'm trying to add a backup folder for the user data files in the install-folder (not my application design, nor my application). Unfortunately every user of this application has their own copy of the data file installed on their system (dedicated machines per user), and the installer has the default file. I would like to create a backup folder so that I can manually (if necessary) go back and retrieve previous versions of the file.
What I'm thinking is
c:\program files (x86)\app*.mdb => c:\program files (x86)\app\backups\201804091125

This will be rushed. Please tell me what is not clear.
Custom Action: In order to implement exactly what you describe, you generally need a custom action. This is always unfortunate since they are very error prone: Why is it a good idea to limit the use of custom actions in my WiX / MSI setups?
Alternative?: If you ask me I would install the database in a component of its own, make the file the key file and set the component to permanent and never overwrite if key path exists.
In the WiX source: for the WiX component element, set these attributes: Permanent="yes" NeverOverwrite="yes"). I am not 100% sure what will happen if you do something stupid such as setting REINSTALLMODE="amus" during installation (force overwrite all files regardless of version). It has been a while since I tested the NeverOverwrite flag. But for normal deployment done the regular way the database file should be left alone and not overwritten.
Custom Action Overview: There are properties called Time and Date that are automatically set in the installer, but the Date property will generally contain characters that are illegal in path names. It is possible to just get the properties and replace the illegal characters. However, the date separation characters are probably different based on regional settings and hence hard to predict. Your code could get messy quickly and testing would be challenging (potentially many locales to test depending on distribution scope - a truly globally capable package is challenging).
I would rather get the date and time some other way - via some programming API call where I can determine what format the data comes back in. You also need to run this custom action elevated in deferred mode to ensure it doesn't fall over with access denied (insufficient user rights for operation). This is always quite a bit of clunk to set up and get working. Maybe try the alternative approach first?
I have long considered adding a custom action to abort the install if REINSTALLMODE="amus" has been specified. I would prefer that and the alternative approach described with "never overwrite" to a custom action doing all this copying.

Related

How can the contents of an installed file marked Permanent="No" be preserved during an upgrade?

Installers of previous versions of our software include a Component File that was NOT marked with Permanent="Yes". Now, we wish to read the pre-upgrade contents of this file during the upgrade process, which will overwrite the file with different contents. Is there a good way to do this?
It would help if you said exactly what you were doing that would cause the file to be overwritten. Some major upgrades (is that what you're doing?) will do a complete uninstall of the product first, followed by a complete install of the newer product. If that's the situation then use a custom action sequenced before RemoveExistingProducts to back up the file somewhere so that your application can retrieve the content, or get the content you need before it's ovewritten.
If you are doing a major upgrade sequenced later (such as afterInstallExecute) or you are doing a patch then it is by no means certain that the file will be overwritten because file overwrite rules will not replace a file that has been updated since it was installed. If the application altered the file then this type of upgrade will not overwrite it:
https://msdn.microsoft.com/en-us/library/windows/desktop/aa370531(v=vs.85).aspx
Or in the case that the file is unchanged since it was installed, change the dates so it appears to be modified, as described here:
https://blogs.msdn.microsoft.com/astebner/2013/05/23/updating-the-last-modified-time-to-prevent-windows-installer-from-updating-an-unversioned-file/
It's also not clear that Permanent=yes is what you want anyway - that would glue the file to the system forever. You may be thinking of NeverOverwrite, but it's typically not required if the app changes the files, and easier to decide at upgrade time (by changing dates) instead of committing to NeverOverwrite when it's sometimes unclear what the product may need in the future.
A comment refers to retrieving the previous version of the product during the upgrade. There are a number of ways to do this:
If you know the ProductCode of the previous version, MsiGetProductInfo (and equivalents in script etc) will return product version values or strings:
https://msdn.microsoft.com/en-us/library/aa370130(v=vs.85).aspx
Or if you'd rather not hardcode the value, MsiEnumProducts passing the UpgradeCode will return a list of installed ProductCodes. This technique is most useful if you have your own bootstrapper or UI where you want to show the user the current installed version.
In a WiX major upgrade the associated property (WIX_UPGRADE_DETECTED) is a list of the ProductCodes detected (usually a list of one) so you can use that to get the version of the product being upgraded. In a small vbscript example, something like:
set installer = CreateObject("WindowsInstaller.Installer")
and:
prodversionstring = installer.productinfo(WIX_UPGRADE_DETECTED, "VersionString")
will get you close.
Assuming this file is a configuration file such as an XML file, I find this is just a tough area of Windows Installer. You ship file version 1, the end user modifies certain attributes and then you ship file version 2 to which you want to preserve those customizations.
The problem is this is a very complex merge. It works somewhat OK if you only care about 1-2 attributes but if the answer is I need to preserve all of it then you are stuck between losing all the customizations or not getting the changes from version 2 of the file.
You could write extensive custom actions to do all this during the installer but I propose there is a better way: Have 2 files.
1 file that is owned by the installer and can always be safely overwritten and 1 file that is owned by the application that overrides are stored in. Think of it like a transformation file. The installer doesn't know about this file so it never overwrites or deletes it. (The very definition of user data from an MSI perspective.)
For example the .NET framework Web.Config schema AppSettings element has a file attribute that was designed to support this nicely.
Specifies a relative path to an external file containing custom
application configuration settings. The specified file contains the
same kind of settings that are specified in the , , and
elements and uses the same key/value pair format as those
elements. The path specified is relative to the main configuration
file. For a Windows Forms application, this would be the binary folder
(such as /bin/debug), not the location of the application
configuration file. For Web Forms applications, the path is relative
to the application root, where the web.config file is located.
Note that the runtime ignores the attribute if the specified file can
not be found.

Wix custom action is failed to load the dll file?

I am trying to do a custom action at the time of msi installation. But the dll required for my custom action is depends on other dlls. At the time of installtion it is giving the error like "a dll required for this install to complete could not run".How can I load the dependent dll files for this custom action to run properly.
The code that I am using is
<CustomAction Id='CheckingPID' BinaryKey='CheckPID' DllEntry='ValidateKey' />
<Binary Id ='CheckPID' SourceFile='$(sys.CURRENTDIR)\LicenseKeyClient_32d.dll'/>
<Binary Id ='CheckPID2' SourceFile='$(sys.CURRENTDIR)\curllib.dll'/>
<Binary Id ='CheckPID3' SourceFile='$(sys.CURRENTDIR)\libsasl.dll'/>
<Binary Id ='CheckPID4' SourceFile='$(sys.CURRENTDIR)\openldap.dll'/>
The files that you add in binary table usually get extracted with temporary names during the installation, so your DLL will not be able to locate the other DLLs you add next to it.
A workaround is to add those DLLs as normal files in the Temp system folder and delete them when the installation ends. This has the limitation that you need to set your custom action as deferred, so it executes after the files are installed, thus your DLLs get copied to Temp folder.
I don't know if wix has a support for temporary files, similar with the one present in Advanced Installer, but if not you could try to write a custom action to simulate it. Basically what Advanced Installer does is to extract those files in the Temp folder the moment the MSI is launched, and also deletes them when the installation is complete. This has the advantage that you can use the temporary files and in immediate custom actions, i.e. well before the files from your package are installed.
Despite Bogdan's excellent answer, allow me to add my 2 cents:
It looks like you are dealing with some form of license key validation? The best way is generally to deal with your license keys in the application itself, unless you want it written to HKLM instead of HKCU - in which case you might need the temporary admin rights generally acquired during installation.
You can also open a HKLM key for writing during setup, and write it from the application though this is generally frowned upon security-wise. This allows you to write a single license key for all users directly from the application.
The application features more flexibility and control of the process of registering your license key, and crucially an easy way to run the process again. From my perspective this is almost always needed for a serious application - often due to trial versions with the eventual need to register the license key at the end of the trial period from within the application itself - instead of uninstalling the application and reinstalling, or running the setup in repair / maintenance mode - which seems extremely clunky.
I have described this issue previously in some more detail: Reasons to deal with licensing in the application rather than the setup.
I'll also add that WiX DTF .NET custom actions really simplify this problem by allowing you to embed content into the self extracting custom action package and make them available in the current directory at runtime. Very easy.
But yes, Glytzhkof is correct. Any licensing / DRM done inside of an MSI is easily defeated. It's best to do this in the app or both. For example I've worked at companies where it's a share responsibility. You can enter one now or later. I've also worked at companies where the license key had bits embedded in it that drove feature selection. It gets complicated fast so try not to have to go down that road.

WiX Installer FileSearch with full file path

I'm creating an MSI using WiX, and that MSI accepts as a user-input property, the path to a file name that the installer logic will be using. I'm trying to validate the property by determining whether that file exists, but with a full file path I can't figure out how to get that to cooperate with the DirectorySearch and FileSearch pattern.
So, say the user runs the MSI like:
msiexec /i myinstaller.msi CUSTOMFILE="C:\test\input.txt"
I would then need to run something like:
<Property Id="CUSTOMFILEEXISTS">
<DirectorySearch
Id="LocationConfigDirSearch"
Path="[CUSTOMFILE_DIR]" Depth="0">
<FileSearch Name="[CUSTOMFILE_FILENAME]"></FileSearch>
</DirectorySearch>
</Property>
But I:
Can't figure out how to split the filename into its parts. Something like Path.GetDirectory([CUSTOMFILE]) and Path.GetFileName([CUSTOMFILE]) would be ideal. or;
Can't figure out how to determine whether the file exists using the full file name as-is. Say for example, a property on DirectorySearch for IgnoreFileName="true", but I know such a property does not exist.
Do I need to go to the extent of writing extension code or a custom action? I'm hoping this is a simple enough requirement that it won't need to go that far.
The FileSearch element is an abstraction for the Signature table in Windows Installer. The FileName column doesn't support the Formatted data type so you can't put a property in that attribute.
What you might be able to do is standardize on a fixed filename and have the user provide a property with the directory path rather then the file path. Then I think you'd be able to use AppSearch to find a file in that directory without writing a custom action.
Otherwise a custom action to do simple discovery without any state changes isn't the worst thing in the world. Just be careful to not introduce any hosting fragility. ActiveScript (VB/JScript) support in Windows Installer is notoriously fragile. I find C#/DTF managed custom actions acceptable but not everyone does. That leaves C/C++ which can be very solid but harder to code. This is a simple CA so it should be pretty straightforward.
Checks that don't make any changes to the target system are actually OK to perform via a custom action VBScript. As long as there are no system changes, there is no need for a rollback feature and a script is actually normally easier to use than complex file searching mechanisms. Just make sure to keep the script simple, and test it well.
A question that comes to mind is what is inside this text file, though, and how it is specified for the install session. Do you just pass it on the command line, or is there a browse dialog to specify the path?
Here is just a simple check file script from a script repository:
Set objFSO = CreateObject("Scripting.FileSystemObject")
If objFSO.FileExists("C:\FSO\ScriptLog.txt") Then
Set objFolder = objFSO.GetFile("C:\FSO\ScriptLog.txt")
Else
'Wscript.Echo "File does not exist."
End If
Note that I don't think you can call Wscript in a VBScript custom action. You will, however, have access to the MSI file's Session object in immediate mode. Deferred mode access is more difficult as explained here, but that's sort of out of scope for the use you indicate.
Without running a custom action to search the system explicitly, whatever the language, another choice would be to write code to create your own AppSearch in the running MSI file. The key would adding the file to the Path table in the DrLocator table. You'd do this before AppSearch of course.
I don't think it would be a big deal to say that file must have a fixed name and be located somewhere such as next to the MSI file or in the user's AppDataFolder, then you can do an AppSearch for the specific file based on [SourceDir] or [AppDataFolder]
Is this a silent install? Is there a reason why you can't add a custom dialog to browse to the file?

At what step of MSI (InstallExecuteSequence) UAC is prompted?

When I execute my MSI with UAC ON the UAC is not prompted for some time. I am trying to read some registry entry in a custom action before "CostFinalize". My Registry read function will consider a default value if registry entry is not found. But in my case the registry entry is there but it fails to read because the key doesn't have read permission for "User". Although Admin have full permission.
Registry read seems to be happening before UAC prompt. How can i make sure UAC is prompted at start only so that registry read can be successful.
Issue explanation
We have an old installer written in WIX. Where we are writing a registry entry for install location something like this
HKLM\Software\CompanyName\Product\Install\CompInstallDir = [InstallDir]\Product\Component.
This registry entry does have permission for Admin only even user doesnt have read permission I dont know why (i didnt write that code). there are some other entries under HKLM\Software\CompanyName\Product\Install
Now I have to make changes in the installer code for upgrade. In which i have to read this install location i.e., [InstallDir]Product\Component and trim it to [InstallDir]. So I already have an existing custom action (from previous installer code only) which reads the registry and sets Property INSTALLDIR, also some other properties and do backup of some config files. This custom action is under "InstallExecuteSequence" which as per my understanding should prompt for UAC if required. This custom action is called before "CostFinalize".
The thing which should have been there in old installer is Writing a registry entry containing only [InstallDir] which wasn't in place. Due to which that custom action is in place which is not a good way of doing, but being legacy code have to maintain it :(
Hope I am able to explain my problem :)
In this SO thread I explain how UAC prompts are triggered.. Basically, you need a bootstrapper, and in its manifest set the execution level accordingly.
Regarding the custom action to read the registry. Why don't you use the built in support from Windows Installer to make a registry search, using AppSearch and RegLocator tables? As a general rule, its not recommended to reinvent the wheel. A default value for the search can be specified by simply defining the property (name of the search) in Property table.
The normal best practice is for the Install UI sequence to run as standard user and for the Install Execute sequence to elevate if the MSI is built to require it. ( For example a per-user install writing to per-user locations might not ever need elevation ).
The other best practice is to use AppSearch to read registry values into properties. The AppSearch also runs in the InstallUI sequence so normally the expectation is that these reads can be performed using standard user permissions.
In your case, you require admin to do the read. In all my years writing hundreds of installers I've never had that requirement. To give you better advice I'd have to ask what is the nature of this registry value and why is it only readable by administrators? After you read it, what do you intend to do with it?
Options include a bootstrapper to elevate the entire installer including UI sequence but that's usually not advised. Otherwise you need a deferred custom action running without impersonation (SYSTEM context) to do the read but at that point you can't set a property so you'd have to use the registry value right there for whatever purpose is intended.
Very strange requirement... I'm detecting a code smell.

How can my WiX uninstall restore a registry value change?

The installer I'm writing using WiX 3.0 uses a RegistryValue element to modify an existing registry value (originally written by our main product). I'm trying to figure out a way to restore the registry value when the user uninstalls my utility. I'd like to avoid using a custom action, but that might be the only recourse? TIA.
I did this. The registry value in question was the application associated to a file extension, but it could be any registry value.
My first idea was to use a "custom action" for install and uninstall to preserve
and restore, respectively, the associated regy state. This
seemed simple enough.
I created a setup project in VS2008 and built the CA's as javascript files. The "on install" script grabbed the existing regy value and stashed it into a well-known place. The "on uninstall" script would look in the well-known place, and then put the value found there, back in the original location.
Easy, right?
There were two problems:
the script that ran during install, to preserve the pre-existing registry value,
runs AFTER the registry has already been updated
with the values for the newly installed thing. So it preserved the new setting instead of the setting that was there before the MSI ran. Not useful.
The script that runs during uninstall, runs AFTER the registry values, and in fact the entire directory subtree,
have been deleted. Including the stashed value. So it had lost its state.
To solve that I wrote another script that
re-orders the custom actions so they run at the proper times.
There's actually one more twist. Obviously, the "Restore" script (on
uninstall) won't work if it is run after the registry entries for the app have been deleted. I can't remember now, why... but I also determined that this script could not run before that. Somehow that wasn't working either.
So, I modified the MSI to run the restore script
twice. In phase 1, it transfers the stashed value to a "parking lot" in the registry.
Then the application's Keys and Values in the registry get deleted, but the parking lot remains. In
phase 2, outside the transactional protection, the restore script retrieves the state from the parking lot, restores
the file association, and then deletes the parking lot.
I can't remember exactly why I needed to do this in 2 steps, but I remember fighting with it for a while before coming up with that solution.
The way it works in development:
set the on install and on uninstall CA's in the VS project
build the VS Setup project
run the post-processing script that modifies the MSI.
When using the MSI, it's a little more complicated than I originally thought but it works.
If you are using WiX, you may have more control over the time and ordering of the steps, so may not need that post-processing step.
Finally, you said you wanted to avoid a CA. To me, CA's are avoided because they are painful to produce in C++, and producing them in .NET is often inappropriate. But, it's pretty simple to use Javascript for CA's. Some people think script is the wrong tool for the CA job. I think that's wrongheaded. I think script is a very good tool for this purpose. And once you can accept script as a good tool, then you don't need to hold your nose about creating a custom CA.
The registry table is incapable of writing a registry value during an uninstall so it is correct that this must be done via custom action. I am of the opinion that using script is bad. Whether you choose to listen is up to you. I can tell you that just the other day I was called into to trouble shoot a situation where some wrote a vbscript CA that was failing because the file system object had been unregistered as part of a security lockdown process.
I suggest C++ or C#/DTF depending on your needs.
Faced with similar issue, need to update the registry value on install and restore to previous value on uninstall.
Is the only solution possible is creating a custom action for this purpose?
I found a extension for wix that has fucntions for this purpose
link to wix extensions