This article provides a simple guide about how to quickly create a WiX installer project for an existing WPF application or another Windows desktop application.
A real project which implements most of the described topics can be downloaded here.
Install WiX toolset
First, install the WiX toolset from the following page:
- https://wix.codeplex.com (this article is based on the WiX Toolset v3.8)
The WiX toolset installer installs a new Visual Studio extension which provides some new project types. In this article, we will use the “Setup Project” project type.
Create a WiX installer project
After installing the WiX toolset, create a new installer project in your Visual Studio solution:
- Right-click your solution and select “Add” / “New project…”.
- Go to the section “Windows Installer XML” and select “Setup Project”.
- Create a new project called “MyApplication.Installer” where “MyApplication” is the name of your application.
- Open the installer project’s properties (right-click on project then “Properties”) and select “.msi” as “Output Type” in the “Installer” tab.
- Remove all files from the installer project so that it is empty.
Include all files from the output directory
This chapter describes how to set up WiX so that all files from the application’s output directory get included in the installer MSI file. Automatic file harvesting is the simplest solution to generate the list of included files.
For the final installer which is used for production, you should think about creating this list manually because the automatically created list may include files which should not be included in the installer – for example .pdb files.
To enable automatic file harvesting, perform the following steps:
- Unload the installer project in the Solution Explorer (right-click on the project and select “Unload project”)
- Right-click the unloaded project and select “Edit MyApplication.Installer.wixproj”
- Add the following XML at the end of the project file.
Target
tag to add or update in the WiX project file:
SourcePath=MyOutputDirectoryPath
Replace the following placeholder with the correct value:
- MyOutputDirectoryPath: The path to the application project’s output directory (e.g.
../MyApplication/bin/$(Configuration)
).
Remarks:
- It is important to set the attribute
RunAsSeparateProcess
to true so that a new process is started which exits after the harvesting. This ensures that no files are locked after harvesting the output directory.
Next steps:
- After inserting or updating the
Target
XML tag, close and save the project file and reload the project. - Add the application project using the “Add reference…” command to the installer project. From now on, when you compile the installer project, the application project is compiled before the installer, then the
Generated.wxs
file is automatically updated. - Recompile the installer project which creates a hidden file called
Generated.wxs
. This file contains references to all files in the application’s output directory. - Select the installer project in the Solution Explorer and click on the “Show all files” button.
- Right-click the
Generated.wxs
file and select “Include in Project”.
Remarks:
- If you want to exclude some files from being harvested, create a new build step where you copy the desired files from the output directory to another directory and harvest the files from this directory.
Create a new WiX installer file
After generating the Generated.wxs
we need to create the main installer file. To do so, create a new, empty WiX installer file called Product.wxs
in the installer project and insert the following XML content:
The inserted XML has to be customized for your needs. Update the following placeholders:
- MyApplicationName: The name of your application.
- MyManufacturerName: The name of your company.
- MyUpgradeCodeGuid: A random GUID to identify the MSI package during upgrades (GUID in the form
XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
). - MyApplicationExecutableId:
- Change this placeholder to the file ID of the application’s executable file. This ID can be found in the
Generated.wxs
file in a tag like
. - To enable automatic versioning, open the
AssemblyInfo.cs
file in the application project and change theAssemblyVersion
attribute to[assembly: AssemblyVersion("1.0.*")]
so that each build generates a new assembly version. Additionally, by removing theAssemblyFileVersion
attribute the file version is automatically set to the assembly version. This way you don’t have to manually increment the file version in your installer project and upgrades work hassle-free.
- Change this placeholder to the file ID of the application’s executable file. This ID can be found in the
Remarks:
- The file IDs in the
Generated.wxs
file do not change when the file is regenerated. - The
Id
attribute of theProduct
tag must be*
so that a new package ID is generated each time the project is compiled. This is required so that each new MSI package has a new ID and the installer can detect whether a MSI package has changed. - The
UpgradeCode
attribute of theProduct
tag must not be changed for different MSI package builds. If the code has been changed, an older installation cannot be upgraded using the new MSI installer. - The
EmbedCab
attribute of theMediaTemplate
tag specifies whether all data is embedded into a single MSI file, otherwise the MSI file also needs the .cab file. - Set
AllowSameVersionUpgrades
toyes
so that MSI packages where only the revision has changed are not treated as new products.
Create a Fragment tag
After creating the Product
tag, define a fragment which describes where the files should be copied to and what the installer should additionally do. Add the following XML after the Product
tag in your Product.wxs
file (replace the Fragment
tags from the XML above):
Update the following placeholders:
- MyCompanyName1: Name of your company directory in the program files directory.
- MyCompanyDirectory2: Name of your company in the Windows Start menu (group name).
- MyCompanyName3: Name of your company to use in the registry.
- MyApplicationName1: Name of your application directory in the program files directory.
- MyApplicationName2: Name of your application to use in the registry.
Now, you can rebuild the project and a working MSI package should be created.
Create a shortcut in the Windows Start menu
The next step shows how to create an application shortcut in the Windows Start menu. First, add an application icon to the Fragment
tag so that it can be used in the WiX’s shortcut tag:
- In your application project, add a new icon for your application with the name
ApplicationIcon.ico
. - Mark the icon file and set the “Build action” to “Content” and “Copy to Output Directory” to “Copy if newer”.
- Optional: Open the application’s project properties and select the added icon as application icon in the “Application” tab
- Now, add the following XML tag as first child of the “Fragment” tag.
The “Icon” tag to insert:
- The icon can now be used in the
Shortcut
tag using the ID “ApplicationIcon”.
In the “Component” tag, add the following “Shortcut” tag:
Update the following placeholders:
- MyApplicationName: The name of the application which should be displayed in the Start menu.
- MyApplicationDescription: The description of the application which is shown in the Start menu as tooltip when hovering over the icon.
- MyApplicationExecutableId: The ID of the application’s .exe file from the
Generated.wxs
file (e.g.filBC52AFB6B1FDA82AFB5FC43E739D7309
).
When installing the generated MSI now, a new Start menu shortcut will be created.
Register file extensions and handle file open
The next step in this article is to register file extensions for the application. These registrations tell the Windows shell which application to open when double clicking on a document in the file explorer.
First, you have to add a new file icon (e.g. FileIcon.ico
) to the application project and change the file properties the same way as for the icon in the previous chapter. Then rebuild the installer project so that the new icon gets included in the Generated.wxs
file. To enable the registration of a new file association, simply insert the following XML tag into the Component
tag in the Product.wxs
file:
Update the following placeholders:
- MyDocumentId: ID of the element (chosen by user, e.g. “TextFileExtension”)
- MyDocumentExtension: Extension of the document (e.g. “txt”)
- MyApplicationExecutableId: File ID of the application’s executable (.exe) file (e.g.
filBC52AFB6B1FDA82AFB5FC43E739D7309
) - MyIconFileId: File ID of the icon which is registered in the
Generated.wxs
file (e.g.filBB90AF377506520F6D8B60A0568383B4
)
Handle file open in the WPF application
To handle file open – triggered for example when double-clicking a file in Windows explorer – simply run the following C# code when the application has been started (e.g. in the main window’s “Loaded” event):
var args = Environment.GetCommandLineArgs();
if (args.Length > 1)
{
var fileName = args[1];
if (File.Exists(fileName))
{
var extension = Path.GetExtension(fileName);
if (extension == ".MyDocumentExtension")
{
// TODO: Open file from fileName
}
}
}
To simplify this, use the classes mentioned in the next chapter.
Avoid multiple instances of the same application
By default, every time a file is opened by double-clicking in the Windows explorer, a new application instance is started – the file is not opened in the existing application process. There is no trivial solution to this problem.
One possible solution is to have your application create a named pipe. On application start, check for the existence of this named pipe. If the pipe exists, open it and use it to send the filename being opened to the already existing application instance, and then terminate the second application instance. If the named pipe does not exist, no other instance of the application is already running and the application opens the file itself.
The FileOpenHandler class from the MyToolkit library helps you implement this mechanism as well as reading the command line arguments described in the previous chapter.
To use this class, simply add the following code in the constructor of your main window:
var fileHandler = new FileOpenHandler();
fileHandler.FileOpen += OnOpenFile;
fileHandler.Initialize(this);
Now, the OnOpenFile
method is called whenever a file should be opened:
public void OnOpenFile(object sender, FileOpenEventArgs args)
{
// TODO: Open file from args.FileName
}
You can download the classes here:
Summary
As you can see, creating a WiX installer project and setting it up does not take that much time. Having a build step which automatically generates a MSI installer provides a big benefit for testing and deploying the newest application version to end-users. Of course, you can do much more then explained in this short article – for example implement a custom installer GUI.
The following list provides some links for further reading:
What is the reason that “The file IDs in the Generated.wxs file do not change when the file is regenerated”?
I tested it by adding/deleting files from the directory, and indeed! the file id did not change. how does that work?
Nice Idee with automatic Version-update with AssemblyVersion atribute.
But sorry i don´t get running your example.
Do you have a complete running example for me?
Im stuck with the replace MyApplicationExecutableId – I get an error that my fileid (copied from generated) is wrong. It would be great with a full example