Saturday, October 1, 2011

How to Build a ClickOnce Installer for Revit Add-Ins

Have you ever wanted to deploy an installation package that did not required Admin privileges for the distribution of your Revit Add-ins? Do you also need a simple means to deploy frequent updates? ClickOnce installers can do just that for you.


Microsoft unveiled the ClickOnce technology way back in 2003 and is great so long as your application is simple and does not require any modifications during install that may require administrative access. ClickOnce works by copying an entire isolated application to the user's profile on install. There is a small issue in that ClickOnce is not available for class library projects (Revit Add-Ins are class library projects).

Here is a workaround that you can use to deploy a ClickOnce application to install your Revit Add-Ins.

From inside your current Revit Add-In Visual Studio solution, add a new Windows Console project named the same as your target project but with a ".Updater" suffix added to the end. Save this new project alongside your current project. This new console project will only be used to copy the Revit Add-In program files into the user's Revit Add-Ins directory under their Windows user profile.



This new project will allow you to generate the published installer that a standard class will not. Open the project settings and click on the "Publish" tab along the left (VB.NET, C# will be different). You can enter a version setting as well as an installation URL where youy will post your updates.


Notice the highlighted path above in the URL section? You need to also enter this in the "Updates" section. Click on the "Updates" button to access the optional updates settings.


You can set the project and publisher settings from the "Options" button shown here.


The next thing that you need to do is add a reference to your main project so that the resulting dll files can be included as part of the installation of this console project. Add a reference to your console project and choose your project from the Projects tab.


Now that you have a reference to this project, you can add the output dll to your ClickOnce installation process. From the Publish tab, click the "Application Files" button and make sure that everything required by your Revit-Add-In is also available within this dialog. Set each file to "Include" that you will require in your main installation.


You may have noticed that the ".addin" file is included in the above list. Be sure to add your addin file to the console project so that it can be included in this list as it is required in order for the Revit Add-In to launch in a Revit session.

You can also add an icon to be used by your installer if you like by setting an icon in the "Application" tab of the console project.

Now that you've got the basic format of the publish project configured, you'll need some code to copy the files into the user's Revit Add-Ins directory. Enter the following code in the default "Module1" that was created automatically in your new console project to do just that:


Imports System.IO
Imports System.Reflection

Module mod1

    Private m_sourcePath As String = Path.GetDirectoryName(Assembly.GetExecutingAssembly.Location)
    Private m_w7 As String = Environment.GetEnvironmentVariable("UserProfile") & "\AppData\Roaming\"
    Private m_xp As String = Environment.GetEnvironmentVariable("UserProfile") & "\Application Data\"

    ''' <summary>
    ''' The Main Function
    ''' </summary>
    ''' <remarks></remarks>
    Sub Main()
        ' Test for Win7
        If Directory.Exists(m_w7) Then
            DoCopy(m_w7)
            Exit Sub
        End If
        ' Test for XP
        If Directory.Exists(m_xp) Then
            DoCopy(m_xp)
            Exit Sub
        End If
        ' Warn on Failure
        MsgBox("Your Operating System was not Properly Detected", MsgBoxStyle.Exclamation, "Installation Failed")
    End Sub

    ''' <summary>
    ''' Copy the Files
    ''' </summary>
    ''' <param name="p_destination"></param>
    ''' <remarks></remarks>
    Private Sub DoCopy(p_destination As String)
        ' Addin path
        Dim m_PathAddin As String = p_destination & "Autodesk\Revit\Addins\2012"
        ' Get Files
        Dim m_di As New DirectoryInfo(m_sourcePath)
        Dim m_FilesAddin As FileInfo() = m_di.GetFiles("*.addin")
        Dim m_FilesDll As FileInfo() = m_di.GetFiles("*.dll")
        For Each x In m_FilesAddin
            Try
                x.CopyTo(m_PathAddin & "\" & x.Name, True)
            Catch ex As Exception
                ' Quiet Fail
            End Try
        Next
        For Each x In m_FilesDll
            Try
                x.CopyTo(m_PathAddin & "\" & x.Name, True)
            Catch ex As Exception
                ' Quiet Fail
            End Try
        Next
    End Sub

End Module

After you've made all the necessary adjustments, you can click the "Publish Now" button in the Publish tab to publish the project. A new Publish directory will be created beneath the directory where your new console project was saved. This is your ClickOnce installer? Copy the entire set of files to either a location on your network or to an FTP on the web where you will host the installations. The ".application" file is the installer. Setup.exe can be used for browsers other than IE.


Each subsequent time that you update your code, click the "Publish Now" button to create a new publish installer. Each sequence will be saved in the same location. Copy the updated files to the location where you are hosting your updates and will become available to your users by executing the "updater" program that was originally installed on your client's machines (Executing the tool from inside Revit will NOT automatically update the tools in this configuration).

Be sure and use Internet Explorer to run the "*.application" ClickOnce installer as this is the only browser that will support this technology so far.

2 comments:

Alan J. said...

Excellent write-up. Going to try and implement this tomorrow!

Alan J. said...

Thanks for the help Don. As a sign of my appreciation here is the conversion of your example VB code I used for my C# implementation. http://pastebin.com/2Bhsr1Q6

Post a Comment

Note: Only a member of this blog may post a comment.