Creating the SOLIDWORKS add-in installer with Windows Installer XML (WiX)

Edit ArticleEdit Article

Downloading and installing WiX framework

Download the WiX installer from the WiX Toolset website

It is recommended to download the latest stable version

Run the installation process

WiX installation dialog
WiX installation dialog

Once installation is complete click the button to install the Visual Studio extension

Installing the Visual Studio extension
Installing the Visual Studio extension

Alternatively extensions can be downloaded directly from the Releases Page or from Visual Studio Marketplace

Adding the installer project

Once WiX framework and Visual Studio extension are installed setup project can be created and compiled directly from the Visual Studio.

Add new project and select Setup Project for WiX vX under the WiX Toolset category

Creating new setup project
Creating new setup project

Visual Studio will generate the default project.

Configuring the project

Follow the steps below to configure the project

Add extension references

Add the reference for WiX extension to use standard pages in installer dialog by clicking the Add Reference... command from the context menu

Add Reference... context menu command
Add Reference... context menu command

Browse to WixUIExtension.dll and WixUtilExtension.dll files located at %wix%\bin

Adding the x64 configuration

By default WiX project is created for x86 deployment. Majority of SOLIDWORKS versions are x64. So it is required to modify the WiX project to support this environment.

Unload the Setup project in Visual Studio and click Edit Project or open the *.wixproj file in any text editor. Modify property group to support x64 environment as shown on the picture below:

Setting the x64 configuration for setup project
Setting the x64 configuration for setup project

Setting the build folder as a preprocessor variable

For the purpose of simplification of linking of files into the setup project it is recommended to create a preprocessor variable in the WiX project which is equal to the build location of the add-in

SourceOutDir=..\Build

Defining the preprocessor variable in WiX project
Defining the preprocessor variable in WiX project

It is allowed and recommended to use relative path to point to the build location

Configuring the product

Product.wxs is the file which contains the configuration of the installer package

Adding the default dialogs

To add the minimal dialogs (i.e. welcome page, installation folder and progress) it is required to add the following lines within the Package node

<UIRef Id="WixUI_InstallDir" />
<UIRef Id="WixUI_Common" />
<Property Id="WIXUI_INSTALLDIR" Value="INSTALLFOLDER" />

Adding the attribution

It is possible to customize the dialog to include icons and banner images

Banner is an image displayed in the header of the installer. This should be a .bmp file with size of 493x58 pixels.

Banner image in installer
Banner image in installer

Dialog background is an image displayed in the background of the installer. This should be a .bmp file with size of 493x312 pixels.

Background image in installer
Background image in installer

Icon can be added as an .ico file and will be assigned to the product in the Programs and Features group in Control Panel.

The following lines needs to be added within the Package node to add the references to the attribution files:

<Icon Id="MainIconId" SourceFile="Resources\icon.ico"/>
<Property Id="ARPPRODUCTICON" Value="MainIconId" />
<WixVariable Id="WixUIBannerBmp" Value="Resources\banner.bmp" />
<WixVariable Id="WixUIDialogBmp" Value="Resources\dialog.bmp" />

Adding End User License Agreement (EULA)

EULA page can be added to the installer by adding the following line within the Package node. Package won't be installed until user agrees on the EULA terms and conditions. EULA must be provided in the Rich Text Format (*.rtf)

<WixVariable Id="WixUILicenseRtf" Value="Resources\eula.rtf" />

Adding add-in registry component group

Add registry component into the ComponentGroup node to add registry key for the add-in

<Component Id="Reg" Guid="{NEW GUID}">
    <RegistryValue Root="HKCU" Key="Software\SolidWorks\AddInsStartup\{ADDIN GUID}" Value="1" Type="integer" Action="write" />
    <RegistryValue Root="HKLM" Key="Software\SolidWorks\Addins\{ADDIN GUID}" Value="0" Type="integer" Action="write" />
    <RegistryValue Root="HKLM" Key="Software\SolidWorks\Addins\{ADDIN GUID}" Name="Description" Value="AddIn description" Type="string" Action="write" />
    <RegistryValue Root="HKLM" Key="Software\SolidWorks\Addins\{ADDIN GUID}" Name="Title" Value="AddIn Title" Type="string" Action="write" />
</Component>

Adding files

Adding the group for SOLIDWORKS interops

Add the SOLIDWORKS interops to be added to the installer by adding the following component into the ComponentGroup node

<Component Id="interops" Guid="{NEW GUID}">
    <File Id='SolidWorks.Interop.sldworks.dllID' Name='SolidWorks.Interop.sldworks.dll' Source ='$(var.SourceOutDir)\SolidWorks.Interop.sldworks.dll'/>
    <File Id='SolidWorks.Interop.swconst.dllID' Name='SolidWorks.Interop.swconst.dll' Source ='$(var.SourceOutDir)\SolidWorks.Interop.swconst.dll'/>
    <File Id='SolidWorks.Interop.swpublished.dllID' Name='SolidWorks.Interop.swpublished.dll' Source ='$(var.SourceOutDir)\SolidWorks.Interop.swpublished.dll'/>
    <File Id='SolidWorksTools.dllID' Name='SolidWorksTools.dll' Source ='$(var.SourceOutDir)\SolidWorksTools.dll'/>
</Component>

$(var.SourceOutDir) variable will be resolved to the build folder defined in Setting the build folder as a preprocessor variable

Adding another files to the installer

If it is required to include any other data or dll files to the installer add another component with files under the ComponentGroup node

<Component Id="files" Guid="{NEW GUID}">
    <File Id='{FileName}ID' Name='{FileName}' Source ='$(var.SourceOutDir)\{FileName}'/>
    ....
</Component>

Automatically add project files

Alternatively files can be added automatically using the harvest tool so it is not required to manually add each file one-by one. Add the following snippet into the *.wixproj file

<Target Name="BeforeBuild">
<HeatDirectory Directory="..\Build" Transforms="DirectoryHeatTransform.xslt" PreprocessorVariable="var.SourceOutDir" OutputFile="Files.wxs" ComponentGroupName="FilesRegGroup" DirectoryRefId="INSTALLFOLDER" AutogenerateGuids="true" ToolPath="$(WixToolPath)" SuppressFragments="true" SuppressRegistry="true" SuppressCom="true" SuppressRootDirectory="true" />
</Target>

Transforms property allows to define the xslt transformation to exclude any files (e.g. pdb or xml) which are not required to be a part of the installer:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:wix="http://schemas.microsoft.com/wix/2006/wi"
    xmlns="http://schemas.microsoft.com/wix/2006/wi"
    version="1.0"
    exclude-result-prefixes="xsl wix">

    <xsl:output method="xml" indent="yes" omit-xml-declaration="yes" />

    <xsl:strip-space elements="*" />

    <xsl:key
        name="AddInToRemove"
        match="wix:Component[ wix:File/@Source = '$(var.SourceOutDir)\CodeStack.Sw.MyToolbar.dll']"
        use="@Id" />

    <xsl:key
        name="TlbToRemove"
        match="wix:Component[ substring( wix:File/@Source, string-length( wix:File/@Source ) - 3 ) = '.tlb' ]"
        use="@Id" />

    <xsl:key
        name="XmlToRemove"
        match="wix:Component[ substring( wix:File/@Source, string-length( wix:File/@Source ) - 3 ) = '.xml' ]"
        use="@Id" />

    <xsl:key
        name="PdbToRemove"
        match="wix:Component[ substring( wix:File/@Source, string-length( wix:File/@Source ) - 3 ) = '.pdb' ]"
        use="@Id" />

    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()" />
        </xsl:copy>
    </xsl:template>

    <xsl:template match="*[ self::wix:Component or self::wix:ComponentRef ][ key( 'AddInToRemove', @Id ) ]" />
    <xsl:template match="*[ self::wix:Component or self::wix:ComponentRef ][ key( 'TlbToRemove', @Id ) ]" />
    <xsl:template match="*[ self::wix:Component or self::wix:ComponentRef ][ key( 'XmlToRemove', @Id ) ]" />
    <xsl:template match="*[ self::wix:Component or self::wix:ComponentRef ][ key( 'PdbToRemove', @Id ) ]" />
</xsl:stylesheet>

Registering COM components

If stand-alone application needs to be deployed and no COM components needs to be registered than this step could be skipped.

SOLIDWORKS add-in must be registered as a COM component. It is recommended to use Harvest tool (heat) instead of custom action with regasm to deploy COM components

Harvest tool can be used in 2 approaches

Approach A: post build action

Add the following line into the post action build event for the projects which requires COM components to be registered

"%WIX%\bin\heat.exe" file "$(TargetPath)" -ag -srd -cg "AddInComRegGroup" -var var.SourceOutDir -dr INSTALLFOLDER -o "..\Setup\AddInReg.wxs"

Approach B: BeforeBuild Target

Add the following snippet directly to *.wixprojfile

<Target Name="BeforeBuild">
    <HeatFile File="..\Build\CodeStack.Sw.MyToolbar.dll" PreprocessorVariable="var.SourceOutDir" OutputFile="..\Setup\AddInReg.wxs" ComponentGroupName="AddInComRegGroup" DirectoryRefId="INSTALLFOLDER" AutogenerateGuids="true" ToolPath="$(WixToolPath)" SuppressFragments="false" SuppressRegistry="false" SuppressCom="false" SuppressRootDirectory="true" />
</Target>

This action will create an AddInReg.wxs with AddInComRegGroup component file with all required information needed to register the COM component on a target machine.

It is recommended to generate this file directly to the setup project folder

Add this file into the WiX project so it gets compiled.

Add the reference to the component into the Feature node

<ComponentGroupRef Id="AddInComRegGroup"/>

Set the Manufacturer attribute of the Product node as this is a mandatory attribute.

Additional parameters

Modify the MediaTemplate node as follows

Generating single .msi installer package

<MediaTemplate EmbedCab="yes"/>

This will allow to generate a single *.msi file for setup.

Setting the default installation folder

Modify the default installation location to link to x64 version of program files ProgramFiles64Folder. Optionally specify the directory for the company name:

<Directory Id="TARGETDIR" Name="SourceDir">
    <Directory Id="ProgramFiles64Folder">
        <Directory Id="CodeStackDirId" Name="CodeStack">
            <Directory Id="INSTALLFOLDER" Name="MyAddIn" />
        </Directory>
    </Directory>
</Directory>

The above will generate a default install location to be equal to %programfiles%\CodeStack\MyAddIn

Automatically building the add-in project when installer is built

By default installer project is not dependent on other projects in solution. It is recommended to add the dependency so all projects are built before the installer project is compiled so it will ensure the latest binaries got added to the .msi package.

Select the installer project and click Project Dependencies...

Project dependencies context menu
Project dependencies context menu

Make sure that the installer project is selected in the drop-down and check add-in project as a depends on reference

Setting the setup project to depend on add-in project
Setting the setup project to depend on add-in project

Compile the installer project. This will generate an .msi package in the output folder.

Once installed add-in is added to the Programs and Features in Control Panel. The installation can be repaired or add-in can be uninstalled from this page.

Product icon is shown in the Programs and Features
Product icon is shown in the Programs and Features

Releasing new version of the product

When new version of binaries are ready it is required to change the Version attribute of the Product node. And it is possible to upgrade the existing installation of the previous version without the need to uninstall the previous one.

It is recommended to keep the version of the installer in sync with the assembly version of the add-in dll.

Template example of Product.wxs file

<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
    <Product Id="*" Name="[Product Name" Language="1033" Version="1.0.0.0" Manufacturer="[Company Name]" UpgradeCode="NEW GUID">
        <Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />

        <MajorUpgrade DowngradeErrorMessage="A newer version of [Product Name] is already installed." />
        <MediaTemplate EmbedCab="yes"/>

        <UIRef Id="WixUI_InstallDir" />
        <UIRef Id="WixUI_Common" />
        <Property Id="WIXUI_INSTALLDIR" Value="INSTALLFOLDER" />
        <Icon Id="MainIconId" SourceFile="Resources\icon.ico"/>
        <Property Id="ARPPRODUCTICON" Value="MainIconId" />
        <WixVariable Id="WixUIBannerBmp" Value="Resources\banner.bmp" />
        <WixVariable Id="WixUIDialogBmp" Value="Resources\dialog.bmp" />
        <WixVariable Id="WixUILicenseRtf" Value="Resources\eula.rtf" />
        
        <Feature Id="ProductFeature" Title="Setup" Level="1">
            <ComponentGroupRef Id="ProductComponents" />
            <ComponentGroupRef Id="AddInComRegGroup"/>
        </Feature>
    </Product>

    <Fragment>
        <Directory Id="TARGETDIR" Name="SourceDir">
            <Directory Id="ProgramFiles64Folder">
                <Directory Id="CodeStackDirId" Name="[Company Name]">
                    <Directory Id="INSTALLFOLDER" Name="[Product Name]" />
                </Directory>
            </Directory>
        </Directory>
    </Fragment>

    <Fragment>
        <ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
            <Component Id="Reg" Guid="{NEW GUID}">
                <RegistryValue Root="HKCU" Key="Software\SolidWorks\AddInsStartup\{ADDIN GUID}" Value="1" Type="integer" Action="write" />
                <RegistryValue Root="HKLM" Key="Software\SolidWorks\Addins\{ADDIN GUID}" Value="0" Type="integer" Action="write" />
                <RegistryValue Root="HKLM" Key="Software\SolidWorks\Addins\{ADDIN GUID}" Name="Description" Value="AddIn description" Type="string" Action="write" />
                <RegistryValue Root="HKLM" Key="Software\SolidWorks\Addins\{ADDIN GUID}" Name="Title" Value="AddIn title" Type="string" Action="write" />
            </Component>
            <Component Id="interops" Guid="{NEW GUID}">
                <File Id='SolidWorks.Interop.sldworks.dllID' Name='SolidWorks.Interop.sldworks.dll' Source ='$(var.SourceOutDir)\SolidWorks.Interop.sldworks.dll'/>
                <File Id='SolidWorks.Interop.swconst.dllID' Name='SolidWorks.Interop.swconst.dll' Source ='$(var.SourceOutDir)\SolidWorks.Interop.swconst.dll'/>
                <File Id='SolidWorks.Interop.swpublished.dllID' Name='SolidWorks.Interop.swpublished.dll' Source ='$(var.SourceOutDir)\SolidWorks.Interop.swpublished.dll'/>
                <File Id='SolidWorksTools.dllID' Name='SolidWorksTools.dll' Source ='$(var.SourceOutDir)\SolidWorksTools.dll'/>
            </Component>
            <Component Id="files" Guid="{NEW GUID}">
                <File Id='File1.dllID' Name='File1.dll' Source ='$(var.SourceOutDir)\File1.dll'/>
            </Component>
        </ComponentGroup>
    </Fragment>
</Wix>

Example projects


Product of Xarial Product of Xarial