Tutorial: Deploy your Enterprise Architect C# add-in with an MSI package

In previous posts I talked about Creating and Testing your Enterprise Architect C# add-in, and how to use the C# add-in template to speed up the development process.

Once created and tested you’ll probably want to install and use the add-in on other computers then your development machine. This tutorial explains how to create an MSI installer package using the open source SharpDevelop and WiX software.

The reason I’m using SharpDevelop as opposed to Visual Studio C# Express is because SharpDevelop is free and open source, and Visual Studio C# Express doesn’t allow to create setup projects, or attach to a running process to debug your add-in.

Requirements

The plan

As explained in Tutorial: Create your first C# Enterprise Architect addin in 10 minutes we need our installer to do three things for us:

  1. Copy the dll’s to a program folder.
  2. Register the add-in dll for COM Interop so Windows knows where to find the add-in dll.
  3. Create the registry key so EA knows about our add-in.

Step 1: Create a setup project

  • Open the solution for your add-in in SharpDevelop. If you have developed the add-in with Visual Studio, that fine. Sharpdevelop uses the exact same project setup as Visual Studio, with the .sln and .csproj files
  • Add a new project and choose for Setup Project – WixUI Minimal
    You can of course choose for another template, but for the purpose of this tutorial the WixUI Minimal is enough.
    New WiX Setup Project
  • SharpDevelop will now have created a new Setup project in your solution containing three files:
    • Files.wxs
      This is were we define wich files and registry keys the installer needs to install
    • license.rtf
      Contains the license for your software
    • Setup.wxs
      Where we define the general setup of the installer like the name, and the components to install.
    • MyAddinProject

Step 2: Edit Setup.wxs

Next we’re going to tell the installer about the application it will install. The .wxs files are actually XML files, and you can edit them directly in SharpDevelop.

  • Open Setup.wxs and edit the highlighted parts
    Setup.wxs
    Notice that we also added a ComponentRef for MyAddinRegEntries.

Step 3: Add the program files

  • Open up the setup files view with View|Setup|Files
  • Rename the installation folder to something sensible
  • Add a component for each ComponentRef we added in the Setup.wxs
  • Right-click on the MyAddinFiles component and choose Add Files…
    MyAddinAddFiles
  •  Now browse to the MyAddin project folder and select all files in the MyAddin\bin\debug or MyAddin\bin\release folder, depending on how you last built your project.
    Not all of these files might be necessary, but I can’t be bothered to figure out exactly which files we need to install the add-in on another computer.
  • This should now have created a line in the files.wxs for each binary file similar to
    <File Id="EAAddinFramework.dll" Name="EAAddinFramework.dll" Source="..\MyAddin\bin\Debug\EAAddinFramework.dll" />
    <File Id="UMLToolingFramework.dll" Name="UMLToolingFramework.dll" Source="..\MyAddin\bin\Debug\UMLToolingFramework.dll" />
    <File Id="EAAddinFramework.pdb" Name="EAAddinFramework.pdb" Source="..\MyAddin\bin\Debug\EAAddinFramework.pdb" />
    <File Id="Interop.EA.dll" Name="Interop.EA.dll" Source="..\MyAddin\bin\Debug\Interop.EA.dll" />
    <File Id="MyAddin.pdb" Name="MyAddin.pdb" Source="..\MyAddin\bin\Debug\MyAddin.pdb" />
    <File Id="MyAddin.tlb" Name="MyAddin.tlb" Source="..\MyAddin\bin\Debug\MyAddin.tlb" />
    <File Id="UMLToolingFramework.pdb" Name="UMLToolingFramework.pdb" Source="..\MyAddin\bin\Debug\UMLToolingFramework.pdb" />
    <File Id="MyAddin.dll" Name="MyAddin.dll" Source="..\MyAddin\bin\Debug\MyAddin.dll" />
    

Step 4: Register MyAddin.dll for COM Interop

The MyAddin.dll is the add-in dll that EA needs to talk to, so this dll needs to be registered for COM Interop. This is the part that is done by regasm.exe when doing a manual installation, but we don’t want to be calling any executables on the target machine during install if we can avoid it. Instead we will just add the same registry keys that regasm.exe creates.

  • Open a command prompt in the folder where MyAddin.dll is located (bin\Debug\)
  • Execute following command:
    "C:\Program Files (x86)\WiX Toolset v3.8\bin\heat.exe" file MyAddin.dll -ag -template fragment -out MyAddin.wxs

    This will have created a file in the same folder with the name MyAddin.wxs.

  • Open that file and copy the contents of the Component tag:
    <Class Id="{10BC65F1-32C0-3ED4-98A0-17661A8C4455}" Context="InprocServer32" Description="MyAddin.MyAddinClass" ThreadingModel="both" ForeignServer="mscoree.dll">
     <ProgId Id="MyAddin.MyAddinClass" Description="MyAddin.MyAddinClass" />
    </Class>
    <File Id="filCC4172BEC1312562EDEF49648E45AE0D" KeyPath="yes" Source="..\MyAddin\bin\Debug\MyAddin.dll" />
    <RegistryValue Root="HKCR" Key="CLSID\{10BC65F1-32C0-3ED4-98A0-17661A8C4455}\Implemented Categories\{62C8FE65-4EBB-45e7-B440-6E39B2CDBF29}" Value="" Type="string" Action="write" />
    <RegistryValue Root="HKCR" Key="CLSID\{10BC65F1-32C0-3ED4-98A0-17661A8C4455}\InprocServer32\1.0.0.0" Name="Class" Value="MyAddin.MyAddinClass" Type="string" Action="write" />
    <RegistryValue Root="HKCR" Key="CLSID\{10BC65F1-32C0-3ED4-98A0-17661A8C4455}\InprocServer32\1.0.0.0" Name="Assembly" Value="MyAddin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" Type="string" Action="write" />
    <RegistryValue Root="HKCR" Key="CLSID\{10BC65F1-32C0-3ED4-98A0-17661A8C4455}\InprocServer32\1.0.0.0" Name="RuntimeVersion" Value="v4.0.30319" Type="string" Action="write" />
    <RegistryValue Root="HKCR" Key="CLSID\{10BC65F1-32C0-3ED4-98A0-17661A8C4455}\InprocServer32\1.0.0.0" Name="CodeBase" Value="file:///[#filCC4172BEC1312562EDEF49648E45AE0D]" Type="string" Action="write" />
    <RegistryValue Root="HKCR" Key="CLSID\{10BC65F1-32C0-3ED4-98A0-17661A8C4455}\InprocServer32" Name="Class" Value="MyAddin.MyAddinClass" Type="string" Action="write" />
    <RegistryValue Root="HKCR" Key="CLSID\{10BC65F1-32C0-3ED4-98A0-17661A8C4455}\InprocServer32" Name="Assembly" Value="MyAddin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" Type="string" Action="write" />
    <RegistryValue Root="HKCR" Key="CLSID\{10BC65F1-32C0-3ED4-98A0-17661A8C4455}\InprocServer32" Name="RuntimeVersion" Value="v4.0.30319" Type="string" Action="write" />
    <RegistryValue Root="HKCR" Key="CLSID\{10BC65F1-32C0-3ED4-98A0-17661A8C4455}\InprocServer32" Name="CodeBase" Value="file:///[#filCC4172BEC1312562EDEF49648E45AE0D]" Type="string" Action="write" />
  • Replace the file tag for MyAddin.dll in your files.wxs by the copied part
  • Add a Name attribute Name=”MyAddin.dll to the file tag for MyAddin.dll so the tag looks like:
    <File Id="filCC4172BEC1312562EDEF49648E45AE0D" Name="MyAddin.dll" KeyPath="yes" Source="..\MyAddin\bin\Debug\MyAddin.dll" /> 

Step 5: Add the EA Addin registry key

This step will instruct the installer to create the add-in registry key that tells EA there is an add-in to load

  • Add following Registry key tag to the MyAddinRegEntries tag in the Files.wxs
    <RegistryKey Root="HKCU" Key="Software\Sparx Systems\EAAddins\MyAddin" Action="createAndRemoveOnUninstall">
    <RegistryValue Type="string" Value="MyAddin.MyAddinClass" />
     </RegistryKey>
    

All xml file editing should now be behind us. The complete Files.wxs should look something like:

<?xml version="1.0"?>
   <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
     <Fragment>
       <DirectoryRef Id="TARGETDIR">
         <Directory Id="ProgramFilesFolder" Name="PFiles">
           <Directory Id="INSTALLDIR" Name="MyAddin">
             <Component Id="MyAddinFiles" Guid="5C526B21-FB62-42AD-9897-F990FEA7E164" DiskId="1">
               <File Id="EAAddinFramework.dll" Name="EAAddinFramework.dll" Source="..\MyAddin\bin\Debug\EAAddinFramework.dll" />
               <File Id="UMLToolingFramework.dll" Name="UMLToolingFramework.dll" Source="..\MyAddin\bin\Debug\UMLToolingFramework.dll" />
               <File Id="EAAddinFramework.pdb" Name="EAAddinFramework.pdb" Source="..\MyAddin\bin\Debug\EAAddinFramework.pdb" />
               <File Id="Interop.EA.dll" Name="Interop.EA.dll" Source="..\MyAddin\bin\Debug\Interop.EA.dll" />
               <File Id="MyAddin.pdb" Name="MyAddin.pdb" Source="..\MyAddin\bin\Debug\MyAddin.pdb" />
               <File Id="MyAddin.tlb" Name="MyAddin.tlb" Source="..\MyAddin\bin\Debug\MyAddin.tlb" />
               <File Id="UMLToolingFramework.pdb" Name="UMLToolingFramework.pdb" Source="..\MyAddin\bin\Debug\UMLToolingFramework.pdb" />
               <Class Id="{10BC65F1-32C0-3ED4-98A0-17661A8C4455}" Context="InprocServer32" Description="MyAddin.MyAddinClass" ThreadingModel="both" ForeignServer="mscoree.dll">
                 <ProgId Id="MyAddin.MyAddinClass" Description="MyAddin.MyAddinClass" />
               </Class>
               <File Id="filCC4172BEC1312562EDEF49648E45AE0D" Name="MyAddin.dll" KeyPath="yes" Source="..\MyAddin\bin\Debug\MyAddin.dll" />
               <RegistryValue Root="HKCR" Key="CLSID\{10BC65F1-32C0-3ED4-98A0-17661A8C4455}\Implemented Categories\{62C8FE65-4EBB-45e7-B440-6E39B2CDBF29}" Value="" Type="string" Action="write" />
               <RegistryValue Root="HKCR" Key="CLSID\{10BC65F1-32C0-3ED4-98A0-17661A8C4455}\InprocServer32\1.0.0.0" Name="Class" Value="MyAddin.MyAddinClass" Type="string" Action="write" />
               <RegistryValue Root="HKCR" Key="CLSID\{10BC65F1-32C0-3ED4-98A0-17661A8C4455}\InprocServer32\1.0.0.0" Name="Assembly" Value="MyAddin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" Type="string" Action="write" />
               <RegistryValue Root="HKCR" Key="CLSID\{10BC65F1-32C0-3ED4-98A0-17661A8C4455}\InprocServer32\1.0.0.0" Name="RuntimeVersion" Value="v4.0.30319" Type="string" Action="write" />
               <RegistryValue Root="HKCR" Key="CLSID\{10BC65F1-32C0-3ED4-98A0-17661A8C4455}\InprocServer32\1.0.0.0" Name="CodeBase" Value="file:///[#filCC4172BEC1312562EDEF49648E45AE0D]" Type="string" Action="write" />
               <RegistryValue Root="HKCR" Key="CLSID\{10BC65F1-32C0-3ED4-98A0-17661A8C4455}\InprocServer32" Name="Class" Value="MyAddin.MyAddinClass" Type="string" Action="write" />
               <RegistryValue Root="HKCR" Key="CLSID\{10BC65F1-32C0-3ED4-98A0-17661A8C4455}\InprocServer32" Name="Assembly" Value="MyAddin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" Type="string" Action="write" />
               <RegistryValue Root="HKCR" Key="CLSID\{10BC65F1-32C0-3ED4-98A0-17661A8C4455}\InprocServer32" Name="RuntimeVersion" Value="v4.0.30319" Type="string" Action="write" />
               <RegistryValue Root="HKCR" Key="CLSID\{10BC65F1-32C0-3ED4-98A0-17661A8C4455}\InprocServer32" Name="CodeBase" Value="file:///[#filCC4172BEC1312562EDEF49648E45AE0D]" Type="string" Action="write" />
             </Component>
             <Component Id="MyAddinRegEntries" Guid="8439A7F4-D42F-43AC-B91D-CBF823A89DDE">
              <RegistryKey Root="HKCU" Key="Software\Sparx Systems\EAAddins\MyAddin" Action="createAndRemoveOnUninstall">
                <RegistryValue Type="string" Value="MyAddin.MyAddinClass" />
              </RegistryKey>
            </Component>
          </Directory>
        </Directory>
      </DirectoryRef>
    </Fragment>
 </Wix>

Step 6: Fill in the license details

  • Open license.rft in wordpad, or another rtf editor and fill in the license details
    Do NOT use Microsoft Word to edit the license.rtf as Word will make a horrible mess of the rtf file.

Step 7: Build and test your setup project

Now all we need to do is build and test the setup project. You can run it right from within Sharpdevelop.

If all is well you should see something similar to this:

More resources

Related blog posts

Source code on GitHub

Sparx Systems

Other

  • Examples in the EA installation folder: C:\Program Files\Sparx Systems\EA\Code Samples

31 thoughts on “Tutorial: Deploy your Enterprise Architect C# add-in with an MSI package

  1. Hello Greet , thank u for this amazing tutorial , i have this error
    “The system cannot find the file’..binDebugKJaddin.dll’.(LGHT0103)”
    So what can i do now !

    Thanks in advance

  2. i have fix the problem . Thank u Greet ,
    the problem is i have forget”KJaddin”to put before bin , which is cannot be seen in ur xml file

    So ur article is just fine , but u need to Edit the File Tag to this

  3. Hi Geert, great post!

    I see the following in the lines generated by Wix’s heat.exe:
    Name=”RuntimeVersion” Value=”v4.0.30319″

    Does that mean that the target user will need to have the .NET framework v4 installed?
    More generally, in order to register an addin dll, do you need to have regasm on your computer (I think regasm is shipped with the .NET framework) or just the standard regsvr32 (any windows)?

    Thanks,

    Stéphane

    1. Stéphane,

      The clients needs .Net, but not for regasm.exe, just to be able to run the add-in.
      The version of .Net it needs depends on the version you compiled your add-in to.

      The installer will not execute regasm.exe on the client, it will just create the same registry entries as regasm.exe does when registering your dll.

      Geert

  4. Thanks for the great post!
    The instructions are clear and easy to implement.

    I would like to enable the user to select if to install the add-in per user or per machine.
    I understand the registry on step 5 should be changed if the preference is per machine to HKLM.

    But how can I show the user the 2 options and set it on runtime using sharpdevelop as you suggest?

    Thanks in advance!

  5. Thanks for the great posts and quick reply.
    I will try to use a different type of Wix setups.

  6. Hello,

    Thanks for the great instructions.

    Following the obvious reply by Tehila1, I understand there is an option to install for all users on the machine using the HKLM registry entry.

    Which registry entry should I modify in order to install for all users that work on a server, like via CITRIX?

    Thanks,
    Dev.

  7. Thanks ,

    I’ve tried to create an installer per machine using the HKLM registry as you suggest.
    I installed the add-in on a 32 bit machine. The user that installed it has administrator permissions.

    The registry entry is added under HKLMSOFTWARESparx SystemsEAAddinsMyAddin – for all users, but the addin is available only for the user how installed the “per machine” installer. For all the rest the addin is exist in the ‘Manage Addins Window’ in EA but with ‘Error- Missing’ message.

    Any help would be highly appreciated!

  8. Should I modify other registry entries so that it works?
    The HKLMSOFTWARESparx SystemsEAAddinsMyAddin is already exist .

    Thanks again.

    1. Brilliant!!!

      In the HKCR of the user that installed the addin, there is entries of the main class. As opposed to other users that are missing anything related to the main class in the HKCR.

      Can you please guide me further how can I overcome this problem, adding to the rest of the users the relevant registry values?

      Dev.

  9. It is probably something to do with the command in the forth step above; The wxs file contains HKCU values for the class GUID. The class definition has probably the same issue.

    How should I change the command “C:Program FilesWindows Installer XML v3.5binheat.exe” file MyAddin.dll -ag -template fragment -out MyAddin.wxs , so that the wxs file created will contain the right values?

    Any help would be highly appreciated!
    Thanks in advance!

  10. Hi! I was doing this tutorial and everything was working well, but after install it I was receiving all time a missing error in EA.

    After a few hours cheking I discover that some of the tags “RegistryValue” generate using “heat.exe” in the file *.wxs had an absolute path in the field “Value” and I saw in your example, you have a path like Value=”file:///[#filCC4172BEC1312562EDEF49648E45AE0D]”.

    So to correct that mistake you need to copy the id of the tag File and paste it in every RegistryValue tag that has an absolute path in the field Value.

    Sounds complicated but you only need to do a replace searching the absolute path that appear in your RegistryValue tags:

    and replacing with the id that appear in the File tag adding [#ID]:

    So they will change from:
    Value=”file:///ABSOLUTE_PATH”
    to
    Value=”file:///[#ID]”

    That worked for me and I hope help someone else 🙂

    And of course thanks a lot, Geert for the tutorial.
    Greetings, Marcos.

  11. Hi Geert,
    I’m using VS2012 and dropped InstallShield as I cannot get the Register COM Interop to work.
    I tried to follow your tutorial however VS and SharpDevelop seem to be very different. Do you think you could suggest the alternative or advise what needs doing in Visual Studio 2012?
    Thanks

    1. Hi Guillaume,

      I know that is it possible now to create Wix installers with Visual Studio as well, but I’ve never done so myself. Surely there must be documentation on the internet about this.
      In this past I did make some VS installers using the standard VS setup project, and that worked fine as well (it was even easier as VS recognises the COM Interop stuff itself, so you don’t have to do the funny stuff with the Wix commands anymore), but I haven’t done any in VS recently, so I can’t help you with VS 2012 specifics.

    2. Hi again.
      I eventually figured how to get it to work based on your information + Wix help. I’ll try to post an article to complete yours for Visual Studio users.

  12. Hi everybody,
    does this way also work with Python. I created my Addin using Python.
    Thanks in advance. 🙂

  13. Geert, is it possible, with only a repository object, in c#, to determine which object, is highlighted in the project browser??

    1. Yes you can, See

    2. GetTreeSelectedElements ()
    3. GetTreeSelectedItem (object SelectedItem)
    4. GetTreeSelectedItemType ()
    5. GetTreeSelectedObject ()
    6. GetTreeSelectedItem ()
    7. GetTreeSelectedPackage ()

Leave a Reply