Rhino Installer Engine - Multiple plugins


(Alexander Schiftner) #1

I was trying to create a RHI installer package for our plugin suite, which consists of multiple .rhp files. That seems not to be supported yet, the installer engine complained about plugins with different Guids in the package. Is there a way to solve the problem, or is it fundamentally impossible to create installer packages containing more than one plugin?

Regards, Alex


(Menno Deij - van Rijswijk) #2

1 RHP => 1 RHI

It is possible to create an MSI installer using e.g. WiX or NSIS (both free tools) to support multiple plug-in installation and registration. Let me know if you need some more info - we are using WiX.


(Alexander Schiftner) #3

Many thanks for your hint. Could you send a small example using WiX. I would be interested how you choose the installation folder, and how one registers the plugins in Rhino.


(Menno Deij - van Rijswijk) #4

For ease of use we have opted a per-user install, so the plug-ins get installed in the user’s roaming folder. This gives the user the ability to install without administrative privileges, which is in our case much easier for them. Otherwise, each install has to be sanctioned by the system administration people, or each install needs to be scheduled for centralised roll-out. Both make us less agile in our deployment.

I hope to post some more details tomorrow, but I will be out of the office until Friday.


(Menno Deij - van Rijswijk) #5

The plug-ins are each in one MSI merge module (*.msm file). These modules are created by WiX with the following input Marin.Common.wxs file:

The plug-in is called “Marin Common” and is in the file Marin.Common.PlugIn.rhp. The plug-in has GUID 8444f238-79fc-454c-b473-770722c1fd9d (please use another GUID for your plug-in to prevent clashes).

<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
  <Module Id="MarinCommonPlugIn" Language="1033" Version="1.1.10.0">
    <Package Id="914032d3-1879-4538-8aa1-531578892263" Manufacturer="MARIN" InstallerVersion="200" InstallScope="perUser" />

    <!-- This next bit defines the plug-in file and registry entry for Rhino -->
    <Directory Id="TARGETDIR" Name="SourceDir">
      <!-- We do a per-user installation to the Roaming AppData folder --> 
      <Directory Id="AppDataFolder" Name="AppDataFolder">
        <!-- Our directory is called MARIN Rhino Plug-Ins -->
        <Directory Id="PLUGINFOLDER" Name="MARIN Rhino Plug-Ins" >
          <!-- this defines the RHP file -->
          <Component Id="MarinCommonPlugIn" Guid="56589603-483d-4172-9a89-c42d2cf94bf7">
            <!-- Install the plug-in from dev/bin/win64/Release to the plug-in folder-->
            <File Id="MarinCommonPlugIn" Name="Marin.Common.PlugIn.rhp" Source="D:/source/RhinoPlugins/dev/bin/win64/Release/Marin.Common.PlugIn.rhp"/>
            
            <!-- this is required because we install the the user profile -->
            <RemoveFolder Id="remPLUGINFOLDER" Directory="PLUGINFOLDER" On="uninstall"/>
                   
            <!-- this is required because we install the the user profile -->
            <RegistryKey Root="HKCU" Key="Software\MARIN\RhinoPlugIns">
                <RegistryValue Name="MarinCommonPlugIn" Value="1" KeyPath="yes" Type="integer"/>
            </RegistryKey>              
        
            <!-- the following will tell Rhinoceros to find the plug-in there on the next run -->
            <RegistryKey Root="HKCU" Key="Software\McNeel\Rhinoceros\5.0x64\Plug-ins\8444f238-79fc-454c-b473-770722c1fd9d">
              <RegistryValue Name="Name" Value="Marin Common" Type="string"/>
              <RegistryValue Name="FileName" Value="[PLUGINFOLDER]Marin.Common.PlugIn.rhp" Type="string"/>
            </RegistryKey>
            
            <!-- when uninstalling, remove the registry entry for this plug-in -->
            <RemoveRegistryKey Root="HKCU" Key="Software\McNeel\Rhinoceros\5.0x64\Plug-ins\8444f238-79fc-454c-b473-770722c1fd9d" Action="removeOnUninstall" />              
          </Component>
        </Directory>
      </Directory>
    </Directory>
  </Module>
</Wix>

To make an MSM merge module using WiX, run the candle program, followed by the light program:

"C:/Program Files (x86)/WiX Toolset v3.8/bin/candle.exe" "Marin.Common.PlugIn.wxs" -o "Marin.Common.PlugIn.wixobj"

The candle program makes a wixobj file.

"C:/Program Files (x86)/WiX Toolset v3.8/bin/light.exe" -ext WixNetFxExtension -spdb "Marin.Common.PlugIn.wixobj" -o "Marin.Common.PlugIn.msm"

The light program makes an MSM file.


(Menno Deij - van Rijswijk) #6

Next, to make the MSI file, you need another WXS file. This WXS file is part of a WXS project in Visual Studio.

This file contains the reference to the plug-in defined above, and three DLL dependencies (GalaSoft.MvvmLight.Extras.WPF4.dll, GalaSoft.MvvmLight.WPF4.dll and log4net.dll). The depencies are defined at the end and referenced to form a group.

It uses some customized UI and a custom icon.

<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
     xmlns:netfx="http://schemas.microsoft.com/wix/NetFxExtension"
     xmlns:ui="http://schemas.microsoft.com/wix/">

  <!-- Use Id=* to generate a new GUID each time. Note that UpgradeCode MUST always stay the same for ever and ever. -->
  <Product Id="*" Name="MARIN Rhino Plug-Ins" Language="1033" Version="1.1.10.0" Manufacturer="MARIN" UpgradeCode="2C3717DF-EEC1-45B6-9E73-9E228D6DD76A">
    <!-- Install perUser -->
    <Package InstallerVersion="200" Compressed="yes" InstallScope="perUser" />
    
    <!-- Customized UI taken literally from http://neilsleightholm.blogspot.nl/2008/08/customised-uis-for-wix.html -->
    <UI Id="WixUI_MondoNoLicense">
      <TextStyle Id="WixUI_Font_Normal" FaceName="Tahoma" Size="8" />
      <TextStyle Id="WixUI_Font_Bigger" FaceName="Tahoma" Size="12" />
      <TextStyle Id="WixUI_Font_Title" FaceName="Tahoma" Size="9" Bold="yes" />

      <Property Id="DefaultUIFont" Value="WixUI_Font_Normal" />
      <Property Id="WixUI_Mode" Value="Mondo" />

      <DialogRef Id="ErrorDlg" />
      <DialogRef Id="FatalError" />
      <DialogRef Id="FilesInUse" />
      <DialogRef Id="MsiRMFilesInUse" />
      <DialogRef Id="PrepareDlg" />
      <DialogRef Id="ProgressDlg" />
      <DialogRef Id="ResumeDlg" />
      <DialogRef Id="UserExit" />

      <Publish Dialog="ExitDialog" Control="Finish" Event="EndDialog" Value="Return" Order="999">1</Publish>

      <Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="SetupTypeDlg">1</Publish>

      <Publish Dialog="SetupTypeDlg" Control="Back" Event="NewDialog" Value="WelcomeDlg">1</Publish>
      <Publish Dialog="SetupTypeDlg" Control="TypicalButton" Event="NewDialog" Value="VerifyReadyDlg">1</Publish>
      <Publish Dialog="SetupTypeDlg" Control="CustomButton" Event="NewDialog" Value="CustomizeDlg">1</Publish>
      <Publish Dialog="SetupTypeDlg" Control="CompleteButton" Event="NewDialog"
               Value="VerifyReadyDlg">1</Publish>

      <Publish Dialog="CustomizeDlg" Control="Back" Event="NewDialog" Value="MaintenanceTypeDlg"
               Order="1">WixUI_InstallMode = "Change"</Publish>
      <Publish Dialog="CustomizeDlg" Control="Back" Event="NewDialog" Value="SetupTypeDlg"
               Order="2">WixUI_InstallMode = "InstallCustom"</Publish>
      <Publish Dialog="CustomizeDlg" Control="Next" Event="NewDialog" Value="VerifyReadyDlg">1</Publish>

      <Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="CustomizeDlg"
               Order="1">WixUI_InstallMode = "InstallCustom"</Publish>
      <Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="SetupTypeDlg"
               Order="2">WixUI_InstallMode = "InstallTypical" OR WixUI_InstallMode = "InstallComplete"</Publish>
      <Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="CustomizeDlg"
               Order="3">WixUI_InstallMode = "Change"</Publish>
      <Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="MaintenanceTypeDlg"
               Order="4">WixUI_InstallMode = "Repair" OR WixUI_InstallMode = "Remove"</Publish>

      <Publish Dialog="MaintenanceWelcomeDlg" Control="Next" Event="NewDialog"
               Value="MaintenanceTypeDlg">1</Publish>

      <Publish Dialog="MaintenanceTypeDlg" Control="ChangeButton" Event="NewDialog"
               Value="CustomizeDlg">1</Publish>
      <Publish Dialog="MaintenanceTypeDlg" Control="RepairButton" Event="NewDialog"
               Value="VerifyReadyDlg">1</Publish>
      <Publish Dialog="MaintenanceTypeDlg" Control="RemoveButton" Event="NewDialog"
               Value="VerifyReadyDlg">1</Publish>
      <Publish Dialog="MaintenanceTypeDlg" Control="Back" Event="NewDialog"
               Value="MaintenanceWelcomeDlg">1</Publish>
    </UI>

    <UIRef Id="WixUI_Common" />

    <!-- Define the MARIN icon to show in the Add/Remove Programs (ARP) dialog-->
    <Icon Id="Marin.ico" SourceFile="Resources/MarinIcon.ico"/>
    <Property Id="ARPPRODUCTICON" Value="Marin.ico" />
    
    <!-- end of customized UI -->
    <MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
    <MediaTemplate EmbedCab="yes"  />

    <!-- Check for .NET 4.0 -->
    <PropertyRef Id="NETFRAMEWORK40FULL"/>
    <Condition Message="This application requires .NET Framework 4.0. Please install the .NET Framework then run this installer again.">
      <![CDATA[Installed OR NETFRAMEWORK40FULL]]>
    </Condition>

    <!-- Check for Rhino 5.0 x64-->
    <Property Id="RHINOX64">
      <RegistrySearch Id="TestAppRegSearch"
                      Root="HKLM"
                      Key="SOFTWARE\McNeel\Rhinoceros\5.0x64\Install"
                      Name="Version"
                      Type="raw" Win64="yes"/> <!-- Win64=yes is REQUIRED! -->
    </Property>

    <!-- TODO: check the actual version of Rhino against the version used to build the plug-ins -->
    <Condition Message="[ProductName] require Rhino 5.0 (64-bit). Please install Rhino then run this installer again.">
      <![CDATA[Installed OR RHINOX64]]>
    </Condition>
    
    <Feature Id="MarinRhinoPlugins" Title="Marin Rhino Plug-Ins" Level="1" >
      <!-- The DLL dependencies -->
      <ComponentGroupRef Id="CMPGRP_Dependencies"/>
      <!-- Common plug-in -->
      <Feature Id="MarinCommon" Title="Common Plug-Ins" Level="2" Absent="disallow">       
        <MergeRef Id="MarinCommonPlugIn"/>
      </Feature>
    </Feature>
  </Product>

  <!-- This is where the merge modules are referenced. Each plug-in creates a merge module -->
  <Fragment Id="FRG_Merge">
    <DirectoryRef Id="TARGETDIR" >
      <Merge Id="MarinCommonPlugIn"      SourceFile="Marin.Common.PlugIn.msm"       Language="1033" DiskId="1"/>
    </DirectoryRef>
  </Fragment>
  
    <!-- This is where the dependencies are grouped. See below for the components -->
  <Fragment Id="FRG_DependenciesGroup">
    <ComponentGroup Id="CMPGRP_Dependencies">
      <ComponentRef Id="DEP_GalaSoftMvvmLightExtrasWPF4" />
      <ComponentRef Id="DEP_GalaSoftMvvmLightWPF4" />
      <ComponentRef Id="DEP_log4net" />      
    </ComponentGroup>
  </Fragment>

  <Fragment Id="FRG_Dependencies">    
    <Directory Id="TARGETDIR" Name="SourceDir">
      <Directory Id="AppDataFolder" Name="AppDataFolder">
        <Directory Id="PLUGINFOLDER" Name="MARIN Rhino Plug-Ins" >

          <Component Id="DEP_GalaSoftMvvmLightExtrasWPF4" Guid="349e759f-d02b-4f46-a96c-9f523959224d">
            <RegistryKey Root="HKCU" Key="Software\Marin\RhinoPlugIns">
              <RegistryValue Name="DEP_GalaSoftMvvmLightExtrasWPF4" Value="1" KeyPath="yes" Type="integer"/>
            </RegistryKey>
            <File Id="GalaSoftMvvmLightExtrasWPF4" Name="GalaSoft.MvvmLight.Extras.WPF4.dll" Source="$(var.Marin.Common.TargetDir)GalaSoft.MvvmLight.Extras.WPF4.dll"/>
          </Component>

          <Component Id="DEP_GalaSoftMvvmLightWPF4" Guid="e193825b-389b-41d3-9ccc-dc08f222be70">
            <RegistryKey Root="HKCU" Key="Software\Marin\RhinoPlugIns">
              <RegistryValue Name="DEP_GalaSoftMvvmLightWPF4" Value="1" KeyPath="yes" Type="integer"/>
            </RegistryKey>
            <File Id="GalaSoftMvvmLightWPF4" Name="GalaSoft.MvvmLight.WPF4.dll" Source="$(var.Marin.Common.TargetDir)GalaSoft.MvvmLight.WPF4.dll"/>
          </Component>

          <Component Id="DEP_log4net" Guid="dc41aa47-37e0-4a4d-a9b7-bc50fc908a2a">
            <RegistryKey Root="HKCU" Key="Software\Marin\RhinoPlugIns">
              <RegistryValue Name="DEP_log4net" Value="1" KeyPath="yes" Type="integer"/>
            </RegistryKey>
            <File Id="log4net" Name="log4net.dll" Source="$(var.Marin.Common.TargetDir)log4net.dll"/>
          </Component>

        </Directory>
      </Directory>
    </Directory>
  </Fragment>
</Wix>

(Alexander Schiftner) #7

Many thanks, this is very helpful!