I have a WIX installer running upgrade.
it completes successfully the vast majority of times. but in some cases I'm getting:
"IO.FileNotFoundException: Could not load file or assembly.... or one of its dependencies. The system cannot find the file specified."
the actual file not being found is not consistent and changes between errors.
Note: I have a WIX installation that wraps several MSIs together. the error I'm getting happens during the upgrade process itself where I run some custom c# code (to configure the machine and the environment). this code is NOT run as a custom action, rather it runs after all the inner MSIs complete (and they complete successfully).
Since the same installer completes successfully almost all the time I'm inclined to think this is an environmental issue but I cannot come up with a plausible theory to even start and test this.
this upgrade process can run on windows server 2008 r2 to the latest windows server.
The installer makes sure all .net framework prerequisites are already installer before proceeding.
Any clue on a possible reason for this to happen will be greatly appreciated.
Quick Brainstorm: 1) You could have downgraded files? 2) you have mismatched component GUIDs? 3) you have removed files without realizing? (could work if older setup version had file set permanent) 4) you have missing pre-requisites? 5) custom actions can have deleted files? 6) virus scanners can have quarantined files?
There are edge and fringe cases. Right now I can only think of transitive conditions for components (msidbComponentAttributesTransitive => component conditions are re-evaluated on reinstall potentially removing the file), but that should not affect major upgrades I think.
Downgraded Binary: It could be an issue of a downgraded binary. Downgrading a binary in your upgrade setup (including a lower version file to replace a higher version from the original setup) can result in files being missing after the installation. Try to run repair from Add / Remove Programs and launch the application again. If this solves the problem and the application launches you very likely have this problem on your hands. An ugly fix that I have recommended for this is to update the version number of an old binary to a higher version using File => Open as resource in Visual Studio. There are other ways, but I use that approach for pragmatic reasons.
Logging: There are several other possibilities. The first thing you should do is to make a proper log file:
msiexec.exe /i C:\Path\Your.msi /L*v C:\Your.log
and then look for entries like these:
MSI (s) (0C:5C) [16:13:25:890]: Disallowing installation of component: {015A4DC1-56F4-562B-96B5-B3BE0D45FA5F} since the same component with higher versioned keyfile exists
MSI (s) (0C:5C) [16:13:25:890]: Disallowing installation of component: {4B6A1404-3892-5BEF-AB47-8FE3149211A4} since the same component with higher versioned keyfile exists
See this old answer from Rob Mensching himself. And here is more from Chris Painter.
Logging How-To: Here is an answer on MSI logging. I would just enable the global logging policy so all MSI operations create a log file in the TEMP folder.
Mismatched Component GUIDs: You should keep component GUIDs stable between setups unless you move files to another location (in the source media - in other words install to a different absolute target path). If you sequence the RemoveExistingProducts action to run late your component referencing errors might cause files to be missing after major upgrade as the setup enforces component rules in this scenario (with early sequencing these rules can be bent).
If you use mismatched component GUIDs - in other words you don't keep the component GUIDs stable for files targeting the same absolute path AND you use late sequencing for the .
When should you change component GUIDs? (recommended).
Related
I want to create an Minor Upgrade for a product, I already define a WIX project that generates the MSI installer .
Directions from Microsoft states that in a Minor Upgrade, the ProductCode should not be changed, but ProductVersion must be changed.
Furthermore I change the AssemblyInfo of my application in order to match the ProductVersion. The change in the AssemblyInfo involves change for the AssemblyVersion and AssemblyFileVersion.
After these changes I generate another installer, and now I have two installers:
Installer_1.0.1.msi (A)
Installer_1.0.2.msi (B)
I installed A (double click) then proceed to Install B (double click), but I got this:
Microsoft states that to apply a minor upgrade I should see how to apply a Small Update, I choose to try Reinstalling the product :
The steps described suggest to use the command
msiexec /I Installer_1.0.2.msi REINSTALL=ALL REINSTALLMODE=vomus
Checking the msiexec command line reference I found that:
v: Runs from source and re-caches the local package
o: Reinstalls if file is missing or if an older version is installed
m: Rewrites all required computer-specific registry entries
u: Rewrite all required user-specific registry entries
s: Overwrites all existing shortcuts
That's what I need!!! (I thought to my self).
Tried the command, the result was:
No file was changed
The version in control panel/Programs changed
The installer cache change and I was not able to uninstall the product from Control Panel/Programs/Uninstall
I proceed to change the command parameter o for a because it Forces all files to be reinstalled.
Same result.
My objective is to be able to change the files without having to manually uninstall the product (This can be achieve with a MajorUpgrade, but it implies the change of the ProductCode).
What am I missing in this process?
UPDATE:
If I setup the ProductCode to be dynamic it works as expected, but it would become the minor upgrade in a major upgrade (According to the technical library).
The question is somewhat older, but the correct answer is:
What you have done, works, if done right, the two main points are:
Package Id should be different of new version (minor upgrade)
(but) use REINSTALLMODE=vemus instead of vamus in a production environment , the latter is a worst practice pattern, and if you use it, then only for a quick test ("all files overwritten, no matter which version").
So the command line is:
msiexec /I Installer_1.0.2.msi REINSTALL=ALL REINSTALLMODE=vemus
This surely works. "Only" (yes) files which have been changed after the first installation (by manual edit or changed with a program) will not be updated (for safety). You will need other methods for this (look for "companion file(s)").
But the described way of a minor upgrade though feasible is not to recommend for a production environment, maybe only for MSI experts.
1.
At least, update the file versions of all files in the setup, then you don't have to rely on "vemus" (or vamus), and moreover, you don't loose the overview and error possibilities are highly minimized.
2.
It is recommended by most sources (and I would agree) to not use Minor Upgrades because Major Upgrades work smoother, especially for non experts.
If install performance is a point, you can even use the uninstall old version after install pattern there (means "only", the action "RemoveExistingProducts" is at the final part of the setup (see MSDH help for this)).
3.
Next step of complexity (but you have to deal with) is using Patches (.msp files) as non full updates.
For Minor Upgrades you have to have same ProductCode and UpgradeCode. But you have change the ProductVersion, Package GUID and also the versions of the binaries (DLL & EXE files) in the MSI package should be greater than the previous version
I have had the pleasure of using ISLE and its now got to the point where I have to post a question on SO. Ohh the headache.
I have tried added the Extended WPF Tookit via nuget and manually to my application but with no luck getting ISLE to include these assemblies when it builds the installer. I have done a dependency scan in ISLE and in both scenario the dependencies are present.
I am using a TeamCity (v8.1.1) build server to automate the build. Everything works fine except that it will not include the above mentioned assemblies in the package.
How do I solve this problem?
Update #1
With some more research it seems that ISLE on the dev. box picked up a wrong version of log4net.dll while the build server found the correct version.
Resolution - Cleared all log4net.dll found in the "%temp%\Temporary asp.net files" folder.
You solve the problem by turning off dependency scanning and take responsibility for knowing what your applications needs to run and what the best way for deploying it. Dynamic installation authoring has never fully worked and it never will. When you take into consideration all the different kinds of apps and the way they take their dependencies it becomes obvious.
The easy button is an illusion.
I am unaware of why ISLE has these inconsistency and obvious logic problems however I have resolved my issue, albeit the solution is shaky.
Issue #1
Firstly I encountered a XamlParseException due to a TypedInitializationException. An assembly could not be loaded and that assembly was noted in the Exception which turned out to be log4net. The log4net assembly was in the folder however it was the wrong version.
Somehow ISLE found an older version of this assembly in the "%temp%\ASP.NET Temporary files" folder and used that in the package. Clear all these files and give ISLE no choice but to use the assembly you have provided. NOTE: Don't get gungho and delete the culprit assembly if found in the folder of one or more of your installed applications - it just might stop working.
Issue #2
I realized that not only were the Extended WPF Toolkit but the output of a dependent console application was missing from the install directory.
You would expect ISLE to find all dependencies however it doesn't.
Based on the advice provided by ##Christopher Painter I added the primary output of the console application dependency manually. I expected this to solve the console dependency issue however it turned out to solve both.
Now does ISLE break internally if the one of the dependencies is unavailable, locked, or some exception occurred while adding to the output causing it to stop at that point and not add any more dependencies. Who knows, but I am sure there are some flaws within ISLE Microsoft if you are going to ship a 3rd party and only a 3rd party installer ensure that it works properly first.
Update #1
Running the build a second time on the TeamCity server resulted in the Extended WPF Toolkit not being added again. So it seems again the ISLE installer is really shaky.
Issue #3
Another option to solve the missing Extended WPF Toolkit assemblies would have been to add then explicitly as dependencies. ISLE however adds a explicit rather than relative path location to these files meaning that you need to recreate the folder structure on your build server (not good).
If anyone has a better solution I would love to hear it. This request also goes out to Flexera Software and hopefully the answer is not to upgrade to the PRO version or pay for support.
We are on the eve of product launch, and at the last minute I am being bombarded with crash reports that appear to be related to our installer, which is a WiX3 project with separate outputs for x86 and x64 builds. These have been an ongoing problem that I always thought were fixed, only to find out that they were still lurking.
The product itself is a collection of binaries that communicate with each other via .Net remoting, including a Windows Service and a small COM component that is loaded as an addon in another app. The service runs as SYSTEM, the COM piece runs in a low-rights context, while the other pieces run in normal user contexts. Other pieces include an third-party COM object library DLL and a shared DLL with the .net Remoting interfaces.
I've observed flat-out weird behavior with MSI, particularly on version upgrades. Between MS' anal strong-name implementation (specifically, the exact version check before loading a given assembly), a documented WiX/MSI bug that sees critical files erased on upgrades (essentially, if a file in the upgrade MSI has the same version number as the existing install, that file is deleted)(edit: having trouble producing said documentation...), and having to work around Wow64 virtualization (x86 MSI can only write to registry/HD locations via Wow64, yet x64 MSIs cannot run on x86 computers...), I am about ready to trash the whole thing and port it over to a different install system.
What I am looking for on tips + tricks, techniques, or suggestions on how to properly do things so that I am not fighting with Windows Installer's twisted sense of logic. I am tired of fighting with WiX/MSI/Windows Installer. All it needs to do is place files and registry keys where I tell it to, upgrade them when appropriate, and don't delete anything until the user uninstalls. Instead, dependencies are deleted willy-nilly, bringing up a whole bunch of uncatchable exceptions (can't wrap a try{} block around function declarations) and GPF'ing the whole app.
I am particularly interested in 'best practices' and examples regarding shared and dependency DLLs, and any tips on making sure if a file needs to go to GAC, that it actually goes to the GAC and stays there until it is appropriate to remove it.
Thanks!
Tom
Start off by reading The Definitive Guide to Windows Installer.
Done? Great. Now think about scrapping your existing install, starting from scratch and filing an application bug for everything you currently have to "workaround" in setup. For nearly every single bit of "twisted logic" that you've been fighting with is there for a purpose, to make your installation more reliable and repairable.
If you don't want the reliability and resiliency of Windows Installer, or if you're trying to bypass it in any way then use something else. Windows Installer does much, much more than simply just "place files and registry keys where I tell it to".
When you're writing an MSI package, you define how you want the target system to look. That's the way it looks after setup, the way it should automatically repair to if a file is deleted or a key data file is corrupted. You define how the system should roll back to if a user later cancels during an upgrade from 1.0 to 2.0. Windows Installer is 100% data driven. From a development point of view this is the hardest concept to understand, you can't just edit a config file or write some more data and expect it to persist. Once you get your head around this, Windows Installer becomes a real piece of cake and you design your programs and features to work within it's limitations, rather than trying to break free of them :)
A few other useful links...
Tao of the Windows Installer, Part 1
Understanding UAC in MSI
Assuming you're on at least Wix 3.0, you can make use of MakeSfxCA.exe to package dependencies in a single DLL. (This was an add-in from the DFT -- Deployment Tools Foundation.) Basically, start off by making sure project is copying your dependent DLLs. Make a CustomAction.config file. Test with a simple .bat file like:
REM MyMakeSfxCA.bat - Run under $(TargetDir); abs. paths reqd.
"%WIX%\SDK\MakeSfxCA" ^
%cd%\Managed_custom_action_pkg.dll ^
"%WIX%\SDK\x86\sfxca.dll" ^
%cd%\Managed_custom_action.dll ^
%cd%\Dependent1.dll ^
%cd%\Dependent2.dll ^
%cd%\Microsoft.Web.Administration.dll ^
%cd%\Microsoft.Deployment.WindowsInstaller.dll ^
%cd%\CustomAction.config
Once that works, convert into a Post-Build Event:
"$(WIX)\SDK\MakeSfxCA" ^
$(TargetDir)\Managed_custom_action_pkg.dll ^
"$(WIX)\SDK\x86\sfxca.dll" ^
$(TargetDir)\Managed_custom_action.dll ^
$(TargetDir)\Dependent1.dll ^
$(TargetDir)\Dependent2.dll ^
$(TargetDir)\Microsoft.Web.Administration.dll ^
$(TargetDir)\Microsoft.Deployment.WindowsInstaller.dll ^
$(TargetDir)\CustomAction.config
In your .wxs file, your Binary Key will look like:
< Binary Id="Managed_custom_action_CA_dll" SourceFile="$(var.Managed_custom_action.TargetDir)$(var.Managed_custom_action.TargetName)_pkg.dll" / >
For they CustomAction.config, you can find examples online such as
This is the best way that I've found when working with managed code.
I'm kind of afraid to step into this one but the limitations are only there if you haven't followed best practices upstream.
Take the "delete files" "bug". I haven't seen this one in a while, but if I recall, this is typically caused by the following scenario:
build A:
file 1: 1.0.0.0
build B:
file 1: 1.0.0.0
Now you go do a major upgrade where you have RemoveExistingProducts scheduled after CostFinalize. FindRelated Products detects the ProductCode to be removed, Costing says oh I see I have 1.0.0.0 but 1.0.0.0 is already installed so nothing for me to do here and then RemoveExistingProducts comes by and removes the old version thereby deleting file1.
I always worked around this one by scheduling RemoveEXistingProducts way earlier then suggested by Microsoft. It's been working for me for years.
As for dependency hell, I manage a software product line that consists of hundreds of features expressed by hundreds of merge modules and thousands of files ( 15,000 + ) consumed by dozens of installers on dozens of various feature, integration, main, maintenance and maintenance_integ branches.
How do I do it? One fragment at a time and lot's of automation, SCM and virtual machines to make sure that everything does actually work the way it's intended.
Being this close to your product shipping, the only "answer" I can offer truely offer you is to let you know people like myself are always available for hire. You'd be suprised how fast some of us can turn projects around and get software shipping.
I am building a C# solution in Visual Studio 2008 that has several projects and project dependencies. I am looking for a way to change dll version numbers ONLY when the code that builds the project changes. I currently use Beyond Compare to compare my locally built version to the production file system. The goal is to ONLY deploy updated dlls. I am using autoincrementing version numbers, and each time you open visual studio and do a build, all dll version numbers increment. The same goes for a full solution rebuild and when a different developer does a build and tries to deploy. Is there a way that i can configure Visual Studio to ONLY increment the build number based on changed file contents? Is there an add in that will do this?It seems a binary comparison of these files will also fail because of the different version numbers within the dlls. Does anyone know of a better tool compare only the contents of dlls?Thanks in advance.
One option is to move to a continuous integration solution such as Cruise Control .Net this allows builds to be triggered on check in to a source control system.
Regarding assembly versioning what I usually do is create a single SolutionVersion.cs (to replace the default assembly version cs) that is linked to each project (use the add existing item but change the button to add as link)
Then I use a NAnt or MSBuild task to take the cruise control build label number and overwrite the SolutionVersion.cs verison numbers before the solution gets built
That way I can take an assembly and trace it back to the code via CruiseControl build version (even better I usually get CC.net to label the source with the same number in source control)
Its not quite what you are asking, but I found this helpful in dealing with large solutions: Versioning Controlled Build. According to its doc it detects the changes you are interested in :
"If there is a file with a more recent timestamp (which means that the source code has been modified after the previous version change), the project will be marked for version update."
The recommended, supportable solution would be for your project to NOT auto-increment the build number using the visual studio way. Then you would need to manually, or write a pre-build script/ MS Build Task to do the increment.
There is an interesting sample in this codeproject article which you should check it out... it involves a prebuild task which does the task of updating the build number based on the day of the year
I would suggest that you look into options that your revision control system provides to embed revision information into source files. I've had enough problems with auto-increment in the past that I promised myself never again. These days I prefer something a little more concrete than a build number though and embed unique identifiers into every product of the build system.
I describe my own system in Embedding mercurial revision information in Visual Studio c# projects automatically. While my solution probably isn't right for you, there were other interesting options suggested in response to my question, so some of the solutions I rejected may, nevertheless, be useful to you, even if you have to adapt them to whatever VCS you use.
In my current project, I'm producing weekly releases. I've been using the technique described in this post to keep the version numbers of all of the assemblies in my project in sync. (I don't presently have any good reason to track the assemblies' version numbers separately, though I'm sure that day will eventually come.)
When I push out a release, I build a new version of the installer. Unlike all of the assemblies, which can get their version numbers from a shared SolutionInfo.cs file, the version number of the installer isn't, as best I can tell, an assembly property. So my release process includes manually advancing the version number in the setup project.
Or, I should say, usually includes doing that. I'd like to turn that into something I can't screw up. I'm finding the documentation of setup and deployment projects to be surprisingly opaque (it was quite a bit harder to find out how to make it possible for the MSI to uninstall properly if the user installed it to a non-default path, which is a pretty freaking common use case to be undocumented) and have no idea if it's even possible to do this.
Any ideas?
Edit:
Just to clarify, this is a Visual Studio setup and deployment project I'm talking about.
CodeProject has a script to set the version number of an MSI file, which you could run in the pre-built step of the setup project. You find it here:
http://www.codeproject.com/KB/install/NewSetupVersion.aspx
More Details
Be aware that with Windows Installer things are a bit more complicated. MSI files (as the one that you create using a VS Setup and Deployment project) not only have a version number but also a product code which is a GUID value. This product code is used by Windows Installer to uniquely identify your product e.g. in Control Panel -> Add Or Remove programs where you can decide to uninstall or repair a product.
However, when changing you MSI version number, this product code must also be changed in a number of cases. MSI technology is poorly documented but you can find some recommendations when to also change the product code on the following MSDN page: http://msdn.microsoft.com/en-us/library/aa367850(VS.85).aspx.
In my projects I always generate a new product code for every new version. The script on CodeProject will also change the product code for you.
And one more thing: Windows Installer only checks the first three places of the version number afaik, anything in the forth place will be ignored, i.e. 2.3.0.1234 is considered equal to 2.3.0.5678. (ProductVersion)
(There is a related article on CodeProject which might also be interesting to you: http://www.codeproject.com/KB/install/VersionVDProj.aspx)
Its going to depend on the installer toolkit you are using.
We use TFS Team Build and WiX v3. I have a custom build task that increments the build number in Team build (5.0.0.X for example), then this version number is pushed to the common AssemblyInfo.cs AssemblyFileVersion field. It is also passed by MSBuild to our solutions/projects as a property which is then passed into WiX and used to update the installer version as well.
We probably will need to do better with the assembly versioning someday as well, but right now this has been working pretty well for us.
I use a workaround for VS2010 Setup projects (.MSI + setup.exe). Open the .vdproj in Notepad and edit the ProductVersion assignment value (3.2.1 in the example below). Save the file and launch VS2010 by double-clicking on the .vdproj file.
"Product"
{
"Name" = "8:Microsoft Visual Studio"
"ProductName" = "..."
...
"ProductVersion" = "8:3.2.1"
"Manufacturer" = "..."
...
}