C# application user settings maintained during update - c#

I have a C# application that currently stores user settings/configurations in a separate xml file. My application uses a wix installer and id like to know if there is a way to preserve the xml file should the user be installing a newer version of the application if they install without first removing the older version. The xml file is stored in the CommonAppData folder and should be removed if the application is uninstalled.
Backup question - Is there a better way of doing this than my current method?
UPDATE
I have implemented the following code:
<CustomAction Id="Cleanup_Files" Directory="CompanyFolder" ExeCommand="cmd /C RD
"[CommonFolder]" /s /q" Execute="deferred" Return="ignore" HideTarget="no"
Impersonate="no" />
<InstallExecuteSequence>
<Custom Action="Cleanup_Files" Before="RemoveFiles" >
Installed AND REMOVE="ALL" AND NOT UPGRADINGPRODUCTCODE
</Custom>
</InstallExecuteSequence>
This removes the CommonFolder directory as required on uninstall but it still removes the directory when upgrading. What changes are necessary to achieve this?

The easiest thing to do is: do not insatall this XML file by the setup, but instead let it create by the application if it is not there. By this, an update will not touch the file.
With this scenario it is also possible to migrate the settings from an older (or newer) version.
However, the file will also remain on uninstall. If it is a requirement to remove it in this case, you can do this by a custom action with a condition like Installed AND REMOVE="ALL" AND NOT UPGRADINGPRODUCTCODE

The solution for me was to follow the advice of Klaus but with some additional changes to my product.wxs file. I had to add 'AllowSameVersionUpgrades="yes"' to the MajorUpgrade section.
<MajorUpgrade AllowSameVersionUpgrades="yes" DowngradeErrorMessage="A newer version of
[ProductName] is already installed." />
Thanks to Klaus for all of their advice!

Related

(Wix installer) how to include custom action dependency

I got 3 projects in Install solution:
Custom Action dll written in c#
Custom Action dll written in c++
Util dll written in c++ also (c# and c++ uses it, as
interoop)
The question is how to add Util.dll into wix installer,
When I copy/paste this dll into C:\ProgramFiles\SystemWOW64\ (the msiexec.exe directory) all works fine, but its a bad workaround which I would to avoid.
What I've tried:
1) Add as reference to the MSI project and include as:
<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Fragment>
<Binary Id="CPlusPlusAction" SourceFile="$(var.CPlusPlusAction.TargetDir)$(var.CPlusPlusAction.TargetName).CA.dll"/>
<Binary Id="CSharpAction" SourceFile="$(var.CSharpAction.TargetPath)"/>
<Binary Id="Utils" SourceFile="$(var.SolutionDir)/$(var.Utils.Configuration)/Utils.dll"/>
<CustomAction Id="FunctionOne" Impersonate="no" Return="ignore" Execute="commit" BinaryKey="CPlusPlusAction" DllEntry="FunctionOne" />
<CustomAction Id="FunctionTwo" Impersonate="no" Return="ignore" Execute="commit" BinaryKey="CSharpAction" DllEntry="FunctionTwo"/>
</Fragment>
</Wix>
2) Add as reference to both projects with CopyLocal = true
I've googled a lot but still can't find the solution (Wix Doc, StackOverflow, msdn etc)
Thanks in advance
For C# managed custom actions I don't think you can add the native dll as a reference. So instead add it as content with action copy and I'm pretty sure DTF will package it up and make it available in the temp directory (current directory of the process) at custom action execution time.
For C++, I use the ISSetup.dll / ISSetupFile pattern from InstallShield or statically link the library if you can.
Thanks for answer guys, but finally I figured it out.
First of all you need to add this Util.dll to Msi binary table:
<Binary Id="Utils" SourceFile="$(var.SolutionDir)/$(var.Utils.Configuration)/Utils.dll"/>
If you are not sure use Orca for seeing it inside .msi file
Second as Christopher said for c# its enaught to RightClick -> Add Existing project -> Utill.dll, then DFS extract it during installation into C:/windows/installation/"CustomActionId" and everything works, but...
problem is with c++ side, the Wix isn't for this language, so you need to care of everything during installation, for example write code which will read the BinaryTable for installer, read data and clean all msdn it's a lot of code
so I prefer to write CLR wrapper for util,
Build Util as lib, so on c++ side its build inside CA.dll, and add as lib to wrapper, so now you can use wrapper.dll in c#

EXE file is not working

I have a .msi (windows installer package) file into my project . I generated .exe file from .msi file successfully but whenever I try to open that .exe file or run as administrator it does nothing . How to solve this? Anything will help regarding this . Please help
UPDATE
Here is my code for .msi file
components.wxs
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
<?include Defines.wxi?>
<Fragment>
<ComponentGroup Id="MenuComponents" Directory="ProductMenuFolder">
<Component Id="ProductMenuComponents" Guid="*">
<!--<Shortcut Id="UninstallPackage" Directory="ProductMenuFolder" Name="Uninstall package"
Target="[System64Folder]msiexec.exe" Arguments="/x {[ProductCode]}" Description="Uninstalls $(var.YourApplicationReference.TargetName)"/>-->
<RemoveFolder Id='ProductMenuFolder' On='uninstall' />
<RegistryValue Root='HKCU' Key='Software\[Manufacturer]\[ProductName]'
Type='string' Value='' KeyPath='yes' />
</Component>
</ComponentGroup>
<ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
<Component Id="FileWatcher">
<File Source="$(var.FileWatcher.TargetPath)" />
<!--Register this file as a Windows service-->
<ServiceInstall Id="ServiceInstaller"
Type="ownProcess"
Description="Sends Incoming mainframe files to the Webservice"
DisplayName="FileWatcher"
Vital="yes"
Start="auto"
ErrorControl="ignore"
Interactive="no"
Name="FileWatcher"
Account="[ACCOUNT]"
Password="[PASSWORD]">
<ServiceConfig Id="svcConfig" DelayedAutoStart="yes" OnInstall="yes" OnReinstall="yes" OnUninstall="no" />
</ServiceInstall>
<!--Set the user to be used by the service-->
<util:User Id="ServiceUser" Name="[ACCOUNT]" Password="[PASSWORD]" CreateUser="no" LogonAsService="yes" UpdateIfExists="yes" />
<!--Added example of how to stop service automatically-->
<ServiceControl Id="ServiceControl" Stop="both" Remove="uninstall" Name="FileWatcher" Wait="yes" />
</Component>
<Component Id="FileWatcher.Files" Guid="*">
<!--<Component Id="MAIDFileWatcher.Files" Guid="*">-->
<File Id="filB93E7D71690869B9CD2D0A479DB69C6C" Source="$(var.FileWatcher.TargetDir)\ExceptionHandling.dll" />
<File Id="fil487232F7A833919419AF9537A4390083" Source="$(var.FileWatcher.TargetDir)\ExceptionHandling.xml" />
<File Id="filDE3649B71309470D2D7C086E0FAABAE8" Source="$(var.FileWatcher.TargetDir)\itextsharp.dll" />
<File Id="filF73350F1AEF9ECF2621D4E63B9823029" Source="$(var.FileWatcher.TargetDir)\FileWatcher.exe.config" KeyPath='yes'/>
</Component>
</ComponentGroup>
product.wxs
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<?include Version.wxi?>
<?include Defines.wxi?>
<Product Id="$(var.PRODUCTCODE)" Name="$(var.PRODUCTNAME)" Language="1033" Version="$(var.REVISION)" Manufacturer="$(var.MANUFACTURER)" UpgradeCode="$(var.UPGRADECODE)">
<Package InstallerVersion="400" Compressed="yes" InstallScope="perMachine" Comments="$(var.COMMENTS)" Description="$(var.DESCRIPTION)" />
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
<MediaTemplate EmbedCab="yes" />
<Feature Id="ProductFeature" Title="$(var.PRODUCTNAME)" Level="1">
<ComponentGroupRef Id="ProductComponents" />
<ComponentGroupRef Id="MenuComponents"/>
</Feature>
<UIRef Id="USERUI" />
<?include Actions.wxi?>
</Product>
</Wix>
You can debug an msi installation with a command line like this:
msiexec /i someapplication.msi /L*vx log.txt
This will run the installer and output log info to a file called log.txt.
See also: Windows Installer Command Line Options
Another pro tip is to debug your installer in a virtual machine. Take a snapshot before installing so you can roll back, or repeat the installation after making code changes and start from a reproducible state. I can't imagine debugging installers without Hyper-V - it's essential to me.
This is basically just shooting from the hip, please ignore whatever is not relevant (maybe check the very last three bullet points first):
Best practice: first of all, you are installing multiple binaries with a single component. This is a violation of component creation best practice.
For something this small I would suggest you use one component per file. This solves all kinds of future problems with patching, upgrades and other things.
What happens if the component rules are broken? Please skim this, or take our word for it and just use one file per component. At least make a separate component for all binaries (required).
A little blurb about the nature and philosophy of component GUIDs: Change my component GUID in wix? Some find it helpful to understand the mysterious and always troublesome component GUIDs.
If you insist on using multiple files per component, make sure that the key file for the component is a versioned file. I would think WiX would auto-magic this.
If you don't have a versioned key file, you could risk the component not installing at all if there are files already in the target location.
If you do have a versioned key file, make sure that your install has a higher version binary than the one it may encounter on disk at the target location (if any). Please read about the MSI file versioning rules for an explanation.
Logging: Does your application have a log feature (by default, or one that you can enable) which you can use for debugging? Maybe to the system's event log? Wouldn't services write there?
Dependencies: Also, did you check the pointers I provided earlier with regards to dependency checking? C# Debug folder when copied to another location does not run the exe.
Checking first the modules view in Visual Studio, and then using Dependencies.exe to check for missing dependencies?
Using procmon.exe is a little involved, but almost always reveals surprises and problems that can be hard to detect in other ways: Registering a CPP dll into COM after installation using Wix Msi installer
Does Fuslogvw.exe tell you anything? (.NET assembly binding failures).
Service Credentials: are you sure that those login credentials are getting applied during installation?
Did you try to set them manually to see if the service will run then? Did you add these properties to the SecureCustomProperties list of properties allowed to pass to deferred installation mode?
I think WiX has "auto-magic" here and does this for you, I forget. Check SecureCustomProperties in the property table of your final, compiled MSI using an appropriate tool, for example Orca.
With that delayed service start setting, is the service even running? (got to mention it at least). Or did you say it crashes on launch?
Hard-coded references: pointers to missing resources.
Did you check all the manifest files and config files (FileWatcher.exe.config) for anything funky that points to resources on your developer box (erroneous, hard-coded references)?
Could there be lacking resource files? (images, dlls, etc...).
Architecture & runtime requirements: is the target computer the same architecture as your developer machine? Just to chalk it up, surely you would see a warning about this?
What is the CPU targeted by your code? Any CPU? Did you try to manually register the files on another machine (a clean virtual machine maybe).
Is there anything special about the problem, target computer? Does it have weird policies? Does it have security software blocking things? Does it lack a common runtime component that is installed on your development computer? (.NET, VC++ runtime, VC runtime, java, etc...). These are the things a procmon.exe session should reveal, or a check with Dependencies.exe should show.
Are you using the notorious FileSystemWatcher .NET class? I have used it only once many years ago, but it caused me a lot of grief and I had to stop using it. It did crash my service file regularly.
I will dig up some links here if you are using this class.
Found a couple for now: FileSystemWatcher events raising twice despite taking measures against it and FileSystemWatcher vs polling to watch for file changes.
When I have installed my EXE under %PROGRAMDATA% I had the same issue
When I have installed my EXE under %PROGRAMFILES% I solved the problem

Trying to read a file and then write a file during a WiX install in a CustomAction

I am having a tough time with this. There are a lot of SO articles about these settings but I'm still stuck.
My goal is to perform two steps:
Read a file that will be physically shipped with the msi. In other words, there will be three files setup.exe, test.msi, specialFile.txt
During the install, I want to create a new file in the install path. C:\Program Files\MyCompany\MyApplication\newFile.txt
The file in step 2 is created by reading something out of specialFile.txt from step 1.
My problem is navigating the murky combinations of WiX settings to enable me to read session variables and have high enough privs to write the file out. This has not been easy.
Here is what I've been doing:
<Binary Id="MyCustomAction.CA.dll" SourceFile="path\to\MyCustomAction.CA.dll" />
<CustomAction Id="MyActionsName"
Return="check"
Execute="deferred"
BinaryKey="MyCustomAction.CA.dll"
DllEntry="MyCustomAction"
Impersonate="no"/>
<CustomAction Id="CustomAction1"
Property="LookupUnattendedXML"
Value="INSTALLFOLDER=[I_Can_Get_This_From_The_Directory_Tags];SOURCEDIR=How_To_Get_This???"/>
<InstallExecuteSequence>
<Custom Action="CustomAction1" Before="MyActionsName" />
<Custom Action="MyActionsName" Before="InstallFinalize">NOT Installed AND NOT PATCH</Custom>
</InstallExecuteSequence>
impersonate="no" in order to have enough privs to write the file
Execute is "deferred" in order to have enough privs to write the file (I don't understand how this works)
The Custom tag is set to Before="InstallFinalize". I tried other settings for this. Before="InstallFinalize" seems to fire after the files have been installed.
Then in the C# code, I appear to need to access session to get what I need. I ask for session["sourceDir"] to figure out where the .msi was launched from and then search for specialFile.txt This won't work with "deferred" turned on. I also access a Directory ID called INSTALLFOLDER which is supposed to refer to the path where the application is installed. It appears to do that. session["INSTALLFOLDER"]
The last wrinkle is that when I execute the .msi directly (no bootstrapper) and with deferred changed to immediate, I am able to read the session["sourceDir"] and it's really the location of the .msi as I wanted. When I use the setup.exe bootstrapper, this same variable points to an internal System directory where it appears that the MSI was copied to. I would like to let the user have both methods of execution (with and without the setup.exe bootstrapper) and somehow be able to access these paths.
UPDATE:
This article and this article about custom properties in deferred actions have enabled me to read the install folder in the custom action (now I know where to write the file to). A remaining challenge is figuring out the location of the MSI so I can read the file without using session["SourceDir"] because the Custom Action is deferred.
This would have solved the issue:
<Binary Id="MyCustomAction.CA.dll" SourceFile="path\to\MyCustomAction.CA.dll" />
<CustomAction Id="MyActionsName"
Return="check"
Execute="deferred"
BinaryKey="MyCustomAction.CA.dll"
DllEntry="MyCustomAction"
Impersonate="no"/>
<CustomAction Id="MyActionsName.CustomActionData"
Property="MyActionsName" <!-- this is important -->
Value="INSTALLFOLDER=[I_Can_Get_This_From_The_Directory_Tags];SOURCEDIR=[SOURCEDIR];ORIGINALDATABASE=[OriginalDatabase]"/>
<InstallExecuteSequence>
<Custom Action="MyActionsName.CustomActionData" Before="MyActionsName" />
<Custom Action="MyActionsName" Before="InstallFinalize">NOT Installed AND NOT PATCH</Custom>
</InstallExecuteSequence>
In the custom action you can access the data set in MyActionsName.CustomActionData via session.CustomActionData["propertyname"]
In general you have to pass each variable/property you want to use of the 'normal' installation explicitly to the deferred custom action to show up in CustomActionData.
I've just seen that I'm very late. But maybe someone else will find this helpful.

Wix ToolSet Patch Creation Using "Patch Creation Properties"

First of all, I'd like to iterate that I am a complete noob regarding installers and patches, and have been living by articles from the interwebz.
Quick background: We've created an installer that "installs" a web app - creates IIS services, databases, etc. For the succeeding releases, we plan on using patches for the minor upgrades. We use .NET C# for this app.
I've been trying to create a patch for the project using the "Patch Creation Properties" tutorial from the wix site: http://wixtoolset.org/documentation/manual/v3/patching/patch_building.html
I managed to create a patch, and it does work, but I've noticed that it only changes html, js, and css files, along with the web config. Any changes that I made on .cs files were not reflected. I'm assuming that DLL files are not being replaced by the patch.
Below is my config for the patch.wxs:
<PatchCreation
Id="{real guid heere}"
CleanWorkingFolder="yes"
OutputPath="C:\Outputpath\patch.pcp"
WholeFilesOnly="yes" >
<PatchInformation
Description="Project 3.0.10 Patch"
Manufacturer="project"/>
<PatchMetadata
AllowRemoval="yes"
Description=" Patch v3.0.10"
ManufacturerName="ManufacturerName"
TargetProductName="TargetProductName"
MoreInfoURL="www.google.com"
Classification="Update"
DisplayName="ManufacturerNamePatch"
MinorUpdateTargetRTM="1"/>
<Family DiskId="5000"
MediaSrcProp="Patch"
Name="patchtest">
<UpgradeImage SourceFile="C:\output\test\new\admin\Setup.msi"
Id="UpgradeImage">
<TargetImage SourceFile="C:\output\test\old\admin\Setup.msi"
Order="2"
Id="TargetImage"
IgnoreMissingFiles="no" />
</UpgradeImage>
</Family>
<PatchSequence PatchFamily="SCMPatchFamily"
Supersede="yes" />
</PatchCreation>
I did a patch install with log, and noticed this:
MSI (s) (18:F0) [17:37:18:316]: File: C:\Location\Website.Web.dll; Won't Overwrite; Won't patch; Existing file is of an equal version
I've been scouring the net for answers and haven't found a fix for this.
Thank you very much.
The clue is in the message! Service packs, patches, hotfixes all depend on updating binaries based on the file version. Not only does this speed things up by not installing files that don't need changing, it ensures that you don't overwrite a higher version with an old version. Creation dates (as in the idea that a file should be replaced because mine is newer) do not apply. File versiions are also useful for identifying whether a client has an up-to-date version. So increment the file version, and if it's managed code you don't need to change AssemblyVersion, just add an AssemblyFileVersion for the files that have actually been changed, incremented above the existing installed versions.
https://msdn.microsoft.com/en-us/library/aa367835(v=vs.85).aspx

WiX dll embedding

I'm trying to embed a .dll into a wix installer that is used by other dlls during the install but will not be installed onto the customer's systems.
I've currently got
<Binary Id="AutomationUtils" SourceFile="AutomationUtils\bin\Release\AutomationUtils.dll" />
...
<CustomAction Id="Install" BinaryKey="InstallUILib" DllEntry="AutomationUtils;RunInstallerDLL" Return="check" Execute="deferred" />
but even if I take the other .dll out - WiX refuses to acknowledge the existance of the AutomationUtils .. even though it builds ok (but falls over when you try and run it).
The BinaryKey='InstallUILib' will refer to a Binary element with Id='InstallUILib'. The CustomAction/#DllEntry attribute is the name of the function entry point into the custom action .dll in the Binary element. That name needs that ::GetProcAddress() can access. I'm pretty sure a function named AutomationUtils;RunInstallerDLL is not valid.
If you're looking to do managed code custom action (where you probably need multiple .dlls) this article is okay. Otherwise, if you're creating a native custom action, I recommend creating a single .dll.

Categories

Resources