EA Simple Current User

Example of simple EA Addin installers


A simple EA Menu installer

For our simple EA menu solution we add a new WiX setup project within VS and are presented with a basic Wix source file product.wxs which we now need to populate with information relating to our requirements.

To be honest I delete the content and replace with a basic working example of the type of installer that I am wanting to create!

Each of the sections of the product.wxs file for our simple installer with no UI is non-covered. The complete Product.wxs can be downloaded from this link; the Sections discussed below are clearly marked within that file - start and end.

Installer requirements

Before starting here is the tasks that we want the installer to perform

  1. Install the file (ASimpleAddIn.dll) in a location allowed by standard users, with user choice (this is achieved by setting a parameter on the command line - see later rather than through a UI)
  2. Define the Sparx Addin registry key for this Addin (Classname ASimpleAddIn.ASimpleMenu)
  3. Register our DLL (notably the public class ASimpleAddIn.ASimpleMenu)

With that knowledge we now need to review other requirements for our installer:

  • Check that the .NET framework (4.5 in our case) is present - otherwise we need to advise
  • Check if there an existing version that can be upgraded
  • Do we want to allow downgrades
  • Will the install be per user or per machine or both

For this example we need will install per user, not allowing downgrades. So what do we need to change/add to the wxs file

Each section of the source file is reviewed below.

Section A -

  <!-- SECTION A - Package definitions and checks -->

  <Product Id="*" Name="SimpleEAMenuInstaller" Language="1033" Version="1.0.0.0" Manufacturer="EXploringEA" UpgradeCode="YOURUPGRADE-GUID-4EA9-80C1-EEFC5473F3CE">
    <Package InstallerVersion="500" Description ="This is the Simple Addin Installer" Compressed="yes" InstallScope="perUser" />
    <!-- The following line prevents downgrade-->
		<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
    <Media Id="1" Cabinet="media1.cab" EmbedCab="yes" />

Then within this are Sections B,C,D,E

Section B - sets the scope to be per user.

By adding the definitions below the installer will place files into locations where a standard user has permissions, notably HKCU in the registry and also the ProgramFilesFolder is a location within the users own ...\appdata\local area\programs

   <Property Id="ALLUSERS" Secure="yes" Value="2"/>
   <Property Id="MSIINSTALLPERUSER" Secure="yes" Value="1" />

Section C - installation directory structure

This section defines the installation directory structure. In our example the and with the knowledge that it is a per user install with the definition of the ProgramFileFolder set by the system we see that the directory structure is:

"C:\Users\username\Appdata\Local\Programs\EXploringEAAddins\SimpleAddIn"


    <!-- SECTION C - installation directory structure --> 
    <Directory Id='TARGETDIR' Name='SourceDir'>
      <Directory Id='ProgramFilesFolder' Name='PFiles'> <!-- This is the root of the program files  for Per User it is ..\Appdata\Local\Programs -->
        <Directory Id='AddIn' Name='EXploringEAAddins'>
          <Directory Id='INSTALLDIR' Name='SimpleAddIn'>
'
'
'
'
         Section D - contained with Section C
.
.


Section D - package components

Within the directory are the components relating to:

  • Creating the installfolder
  • Setting our Sparx Addin key
  • An optional registry key to store the location where our DLL has been installed (not essential but I have often found useful to keep track)

Each of these components also includes a definition to remove the item on uninstall.


            <!-- This creates and removes the install folder at thr INSTALLDIR location -->
            <Component Id="InstallFolder" Guid="YOUR_GUID1-442D-A0F8-E48F0AC4BDFD" KeyPath="yes"> 
              <CreateFolder />
              <RemoveFile Id="PurgeAppFolder" Name="*.*" On="uninstall" />
            </Component>

            <!-- This adds and removes our Sparx AddIn key -->
            <Component Id="Sparx" Guid ="YOUR_GUID2-4BE0-86B8-875E413E021E" >
              <RegistryKey Root="HKCU" Key="Software\Sparx Systems\EAAddins\NameOfSimpleAddIn" ForceCreateOnInstall="yes" ForceDeleteOnUninstall="yes"> <!-- Name of my key-->
                <RegistryValue Type="string" Value="ASimpleAddin.ASimpleEAMenu" KeyPath="yes" /> <!-- This is the class name that Sparx will look for -->
              </RegistryKey>
              <RemoveRegistryKey Id='SparxKey' Root="HKCU" Key='Software\Sparx Systems\EAAddins\NameOfSimpleAddIn' Action="removeOnUninstall"/>
            </Component>

            <!-- We add/remove a key that saves our installed program location (for our use) e.g. we may put other files in that location -->
            <Component Id="ProgramLocation" Guid="YOUR_GUID3-4552-9EA9-164F11839699" > 
              <RegistryKey Root="HKCU" Key="Software\EXploringEA\SimpleAddIn">
                <RegistryValue Type='string' Name='ProgramDir' Value='[INSTALLDIR]' KeyPath="yes"/> <!-- Sets the key ProgramDir to Program directory-->
              </RegistryKey>
              <RemoveRegistryKey Id='ProgLoc' Root="HKCU" Key='Software\EXploringEA\SimpleAddIn' Action="removeOnUninstall"/>
            </Component>

Section E - package features

This section defines those components which will be placed in the installer. Now it is vital that all components in the Wxs file are include. You may note that there is no component within this source file for DLLClasses. That is because this is contained in another wxs file that is generated automatically from our dll using the WiX heat tool - see below.

    <!-- Features define which components are in the package these are defined below-->
    <Feature Id="ProductFeature" Title="SimpleEAMenuInstaller" Level="1">
      <ComponentGroupRef Id="DLLClasses" />
      <ComponentRef Id="InstallFolder" />
      <ComponentRef Id="Sparx" />
      <ComponentRef Id='ProgramLocation' />
    </Feature>

Capture the public classes and creating DLLClasse

To ensure that the public classes are captured use is made of the heat tool. The command is entered in the pre-build events for your installer project.

"C:\Program Files (x86)\WiX Toolset v3.10\bin\heat.exe" file "$(SolutionDir)E001_ASimpleEAMenu\bin\$(Platform)\$(Configuration)\ASimpleAddIn.dll" -v -dr INSTALLDIR -srd -gg -g1 -cg DLLClasses -suid  -var "var.E001_ASimpleEAMenu.TargetDir" -out "$(ProjectDir)DLLClasses.dll.wxs"

This example command takes as import our dll file and creates a wxs source file (DLLClasses.dll.wxs). This source file will include the required references (see below), and defined it as a component grup (-cg) DLLClasses, which is referenced in the features section of the product source file.

As mentioned elsewhere you need to:

  • Include a reference the project containing the DLL
  • Ensure that paths in the heat are modified to reflect your solution

Generated file

This is the file DLLClasses.dll.wxs (note name is not special but must end .wxs and be used consistently where referenced.

Importantly note it includes the registry keys that we need to ensure are set.

<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
    <Fragment>
        <DirectoryRef Id="INSTALLDIR" />
    </Fragment>
    <Fragment>
        <ComponentGroup Id="DLLClasses">
            <Component Id="ASimpleAddIn.dll" Directory="INSTALLDIR" Guid="YOUR_GUID4-4FEB-8C06-8573DABF7F88">
                <Class Id="{2600790F-D0E2-3A3C-981A-1008CE94D968}" Context="InprocServer32" Description="ASimpleAddIn.ASimpleEAMenu" ThreadingModel="both" ForeignServer="mscoree.dll">
                    <ProgId Id="ASimpleAddIn.ASimpleEAMenu" Description="ASimpleAddIn.ASimpleEAMenu" />
                </Class>
                <File Id="ASimpleAddIn.dll" KeyPath="yes" Source="$(var.E001_ASimpleEAMenu.TargetDir)\ASimpleAddIn.dll" />
                <RegistryValue Root="HKCR" Key="CLSID\{nnnn-D0E2-3A3C-981A-1008CE94D968}\Implemented Categories\{mmmmm-4EBB-45e7-B440-6E39B2CDBF29}" Value="" Type="string" Action="write" />
                <RegistryValue Root="HKCR" Key="CLSID\{nnnn-D0E2-3A3C-981A-1008CE94D968}\InprocServer32\1.0.0.0" Name="Class" Value="ASimpleAddIn.ASimpleEAMenu" Type="string" Action="write" />
                <RegistryValue Root="HKCR" Key="CLSID\{nnnn-D0E2-3A3C-981A-1008CE94D968}\InprocServer32\1.0.0.0" Name="Assembly" Value="ASimpleAddIn, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" Type="string" Action="write" />
                <RegistryValue Root="HKCR" Key="CLSID\{nnnn-D0E2-3A3C-981A-1008CE94D968}\InprocServer32\1.0.0.0" Name="RuntimeVersion" Value="v4.0.30319" Type="string" Action="write" />
                <RegistryValue Root="HKCR" Key="CLSID\{nnnn-D0E2-3A3C-981A-1008CE94D968}\InprocServer32\1.0.0.0" Name="CodeBase" Value="file:///[#ASimpleAddIn.dll]" Type="string" Action="write" />
                <RegistryValue Root="HKCR" Key="CLSID\{nnnn-D0E2-3A3C-981A-1008CE94D968}\InprocServer32" Name="Class" Value="ASimpleAddIn.ASimpleEAMenu" Type="string" Action="write" />
                <RegistryValue Root="HKCR" Key="CLSID\{nnnn-D0E2-3A3C-981A-1008CE94D968}\InprocServer32" Name="Assembly" Value="ASimpleAddIn, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" Type="string" Action="write" />
                <RegistryValue Root="HKCR" Key="CLSID\{nnnn-D0E2-3A3C-981A-1008CE94D968}\InprocServer32" Name="RuntimeVersion" Value="v4.0.30319" Type="string" Action="write" />
                <RegistryValue Root="HKCR" Key="CLSID\{nnnn-D0E2-3A3C-981A-1008CE94D968}\InprocServer32" Name="CodeBase" Value="file:///[#ASimpleAddIn.dll]" Type="string" Action="write" />
            </Component>
        </ComponentGroup>
    </Fragment>
</Wix>

Building the installer

Before building the installer add a reference to the dll project.

Then when built the 1st time it will fail as the file DLLClasses.dll.wxs doesn't exist. So run and after running this file should be generated so can now be added to the setup project.

Rerun the build and at least that issue will be resolved.


Running the installer

You can double click on the msi file to perform the installation of the DLL. If you run the installer in this way it will not have any parameters and it will install our DLL into a location within the users Appdata\local\programs\... area. To install elsewhere use can be made of the /i swicth to specify the installation directory as illustrated below.

To address this issue use the command line options to specify the install directory (/i) within the current users area and .. it should work.

C:\Windows\System32\msiexec /i SimpleEAMenuInstaller.msi INSTALLDIR="C:\users\adrian\Downloads\SAI" /q

NOTE: Worth becomming familiar with the msiexec command line options as they can be useful.


Uninstall

To uninstall the dll a command as below can be used.

C:\Windows\System32\msiexec /uninstall SimpleEAMenuInstaller.msi