Creating Setup Packages


Creating a setup package to install a VSTO customized document on a user's local machine requires us to build a couple of custom installer classes. We need to update the application manifest stored in the document to refer to the location on the user's machine, and we need to update the user's security policy. Let's walk through all the steps required to add a setup package to a customized spreadsheetsay, an expense-reporting application.

Open the solution for the customized document, and right-click the solution (the root of the tree) in Solution Explorer. Choose Add > New Project, and create a setup project as shown in Figure 20.4.

Figure 20.4. Creating a setup project.


Tip

This step is not necessary if you have created an Outlook Add-In VSTO project. Visual Studio will automatically create an installer project that installs the DLL, creates a manifest, and updates the Outlook add-in registry key for you. You still need to ensure that the right security policy is rolled out, however, and that the VSTO runtime assemblies are installed on the client machines.


Use the Properties pane for the setup project to customize strings such as the author, description, and so on, as shown in Figure 20.5.

Figure 20.5. Setting setup-project properties.


We have not yet told the setup project what files it is going to be setting up. We want it to set up all the files produced by the expense-report project in this solution.

Right-click the setup project, and select Add > Project Output to view the Add Project Output Group dialog box, shown in Figure 20.6.

Figure 20.6. Telling the setup project which files to set up.


Select the Primary Output files associated with the ExpenseReport project, and click OK.

At this point, if you want the users to take responsibility for installing the customization to a location that they trust and do not care that they will have to copy around the customization assembly if they want to move the spreadsheet, you are done. You can build and execute the setup package, and it will copy the necessary files to the user's machine just as they are on the development machine.

You probably want to set the codebase in the application manifest, however, so that it refers to the installation location rather than the current directory. You also probably want to set the user's security policy so that the customization location is fully trusted. That way, the user can copy the document around without worrying about dragging the customization along with it.

To do that, we create yet another project in this solution. Right-click the solution in Solution Explorer again, and create a new, empty Visual Basic project (in the Visual Basic > Windows branch of the tree view of the Add New Project dialog box) called CustomSetup. When you have the project, right-click it, and select the Properties pane for the project. Change the output type to Class Library, as shown in Figure 20.7.

Figure 20.7. Setting the custom installer class project to build a class library.


Right-click the project again, and choose Add > New Item. Add a new Installer Class, as shown in Figure 20.8. In fact, add two: one for the security change, and one for the application manifest change.

Figure 20.8. Adding custom installer classes.


Right-click the References node in the CustomSetup project's tree view, and add a reference to Microsoft.VisualStudio.Tools.Applications.Runtime; we are going to need to create the ServerDocument class, so we need a reference to the VSTO runtime library. Finally, right-click the application manifest installer, and select View Code. Now the Visual Studio IDE should look something like Figure 20.9.

Figure 20.9. Editing the custom installer classes.


Before we get into adding code to these custom actions, however, let's tell the installer about them. Right-click the installer project in Server Explorer, and choose View > Custom Actions. In the Custom Actions viewer, right-click Install, and choose Add Custom Action to view the custom action dialog box shown in Figure 20.10.

Figure 20.10. Selecting the custom install actions.


Click Application Folder and then Primary Output from CustomSetup. Doing so tells the setup project that it should look for Installer classes decorated with the RunInstaller attribute in the assembly produced by the CustomSetup project. As you can see from looking at the hidden partial class associated with the Installer class, both the new custom install action classes are decorated with this attribute.

We must do one more thing to get the custom install actions working properly: They need to know the name of the assembly, the name of the document, and where they are located. To pass these strings from the installer to the custom action, we add the strings to the CustomActionData property of the custom action just created, as shown in Figure 20.11.

Figure 20.11. Setting the custom action data.


The custom action data consists of a set of keys and values:

/custassembly="[TARGETDIR]\ExpenseReport.dll" /custdoc="[TARGETDIR]\ExpenseReport.xls" 


Finally, we are all set up to write the custom installation actions. First, we write the application manifest editor shown in Listing 20.4. We get the strings passed by the main installer out of the installation context object, create a ServerDocument on the installed document, and set the assembly path to the absolute path. Finally, because this is a derived class, we make sure that we call the base class install method in case it does anything interesting (such as write a success message to a log file).

Listing 20.4. Creating an Application Manifest Editor Custom Install Action with ServerDocument

Imports System.ComponentModel Imports System.Configuration.Install Imports Microsoft.VisualStudio.Tools.Applications.Runtime Public Class AppManifestInstaller   Inherits Installer   Public Sub New()     MyBase.New()     'This call is required by the Component Designer.     InitializeComponent()   End Sub   Public Overrides Sub Install( _     ByVal stateSaver As System.Collections.IDictionary)     Dim assemblyPath As String = _       Me.Context.Parameters("custassembly")     Dim documentPath As String = Me.Context.Parameters("custdoc")     Dim sd As ServerDocument = New ServerDocument(documentPath, _         True, System.IO.FileAccess.ReadWrite)    Try      sd.AppManifest.Dependency.AssemblyPath = assemblyPath      sd.Save()    Finally      sd.Close()    End Try    MyBase.Install(stateSaver)   End Sub End Class 


Second, we write code similar to the code we wrote in Chapter 19, ".NET Code Security," to set the local security policy. This time, we set a user security policy that trusts the installation directory explicitly, as shown in Listing 20.5.

Listing 20.5. A Custom Install Action Class to Set Local Security Policy

Imports System.Collections Imports System.ComponentModel Imports System.Configuration.Install Imports System.Security Imports System.Security.Policy Imports Microsoft.VisualStudio.Tools.Applications.Runtime Public Class SecurityInstaller   Inherits Installer   Public Sub New()     MyBase.New()     'This call is required by the Component Designer.     InitializeComponent()   End Sub   Public Overrides Sub Install( _     ByVal stateSaver As System.Collections.IDictionary)     Dim enterprisePolicyLevel As PolicyLevel     Dim machinePolicyLevel As PolicyLevel     Dim userPolicyLevel As PolicyLevel     Dim assemblyGroup As CodeGroup     Dim assemblyCondition As UrlMembershipCondition     Dim policyStatement As PolicyStatement     Dim fullTrust As PermissionSet     Dim assemblyPath As String = _       Me.Context.Parameters("custassembly")     ' Obtain the three policy levels:     Dim policyEnumerator As IEnumerator     policyEnumerator = SecurityManager.PolicyHierarchy()     policyEnumerator.MoveNext()     enterprisePolicyLevel = CType( _       policyEnumerator.Current, PolicyLevel)     policyEnumerator.MoveNext()     machinePolicyLevel = CType( _       policyEnumerator.Current, PolicyLevel)     policyEnumerator.MoveNext()     userPolicyLevel = CType( _       policyEnumerator.Current, PolicyLevel)     ' Create a new group by combining a permission set with a     ' membership condition:     fullTrust = userPolicyLevel. _       GetNamedPermissionSet("FullTrust")     policyStatement = New PolicyStatement(fullTrust, _         PolicyStatementAttribute.Nothing)     assemblyCondition = New UrlMembershipCondition(assemblyPath)     assemblyGroup = New UnionCodeGroup( _         assemblyCondition, policyStatement)     ' Add the new policy to the root:     userPolicyLevel.RootCodeGroup.AddChild(assemblyGroup)     SecurityManager.SavePolicy()     MyBase.Install(stateSaver)   End Sub End Class 


If you build all three projects, right-click the installation project, and choose Install, you will see how the Installation Wizard allows the user to select the location, copies the files over, and then updates the user's security policy and sets the assembly codebase in the embedded application manifest.

This section has given a bare-bones skeleton of a customized installation program, of course. A more robust installer includes features such as custom logging, better error handling, user-interface elements, rollback/uninstall when things go wrong, and so on.

Advanced Topic: Deploying Network Solutions to Be Cached Locally

This chapter began by noting that VSTO supports two main deployment scenarios: Local install ensures that customizations always work, even when the user is not connected to the network, and network installs ensure that the user always has the latest version. It would be nice to have the best of both worlds: the latest version whenever you are online plus the ability to work offline.

If your users always need to be able to run the latest version of the customization while they are online or offline, and also want to have one centralized point at which the customization can be updated, there are two principal techniques for doing so: Deploy the customization to a Web server, and deploy it to an IntelliMirror shared directory.

IntelliMirror Versus Web Caching

If the customization is deployed to a Web server, the Visual Studio runtime loader keeps a local copy of the assembly and configuration file in the Internet Explorer cache so that the customization is available when Internet Explorer is offline. Similarly, a locally cached IntelliMirror share makes the network customization seamlessly available even when the network share is not available.

All other things being equal, the IntelliMirror technique is preferred over the Web server technique, for several reasons. Suppose that you deploy your customization assembly to http://accounting, a local intranet Web server. A user runs the customized document, which downloads the customization assembly from the intranet site and caches a copy in the Internet Explorer cache. Then the user unplugs his laptop from the wall, heads to the local library, and connects to the library's free wireless networking service. Now when the user tries to run the customized document, the .NET Framework will not load the customization assembly out of the Internet Explorer cache, because Internet Explorer believes that it is connected to the network. Instead, the .NET Framework attempts to connect to the intranet server, fails, and prompts the user to go offline to use the locally cached copy. Then the user faces the unfortunate choice of either not running the customization or putting Internet Explorer into offline mode, negating the benefits of having wireless Internet access.

Also, because the Web-server-caching scenario puts the customization assembly in the Internet Explorer cache, anything that causes the cache to be cleared destroys the cached customization assembly along with everything else. Many users clear their Web caches frequently when the caches get too large, and it is very easy to delete a cached customization assembly accidentally.

Finally, a further inconvenience of the Web-caching scenario is that all customizations must have a configuration file associated with them for the offline scenario to work. The next section discusses why.

Therefore, all other things being equal, if you want a hybrid online/offline scenario, the IntelliMirror technique is the preferred one. IntelliMirror shares pay no attention to the state of the Internet Explorer cache or online status.

Why Do We Need a Configuration File?

One of the goals explicitly stated earlier was to be able to have code live up on a Web server, so that it was always up to date, yet be able to access the code when the machine is disconnected. To achieve this goal, the first time the remote code is run, it is downloaded into a local cache. If you run the code again while connected, VSTO checks to ensure that the latest version is downloaded; if offline, VSTO runs the cached code.

Consider this scenario: Your customization assembly is on a Web server along with a configuration file. The customization assembly uses version 1.0.0.0 of a strong-named assembly containing some useful routines you have written. The first time the user runs the application, the customization assembly and configuration file are downloaded and cached. The user goes offline, but the customization continues to work because the cached assembly and configuration file are available.

So far, everything is good. Unfortunately, one day you discover a serious security hole in your library. You fix it and release version 1.0.1.0 of the library. Every customization, however, still attempts to load the old code because the customization assembly was built against the old version. You would rather not go to the trouble of recompiling what might be hundreds or thousands of customizations against the new library; instead, you just update their server-side configuration files to say that the new version should be loaded when the old version is requested. While the user is offline, of course, he will still be running the insecure code, but there's nothing anyone can do about that. When the user goes back online and runs the code, the new configuration file can be downloaded, the new library is installed, and everyone is happy again.

That scenario is reason enough always to use configuration files; it is very handy to be able to change the assembly loading policy easily. But why not create a configuration file only when you find yourself in this unfortunate situation? Why do we require you always to create a configuration file if you want to be able to run server-side code while offline?

Well, suppose that you did not create a configuration file; let's go through that scenario again. Your customization assembly is on the Web, without a configuration file, and uses buggy version 1.0.0.0 of your library assembly. The user runs the application for the first time. The loader finds the customization assembly, caches it, and determines that there is no configuration file on the server. Then the user goes offline. You discover your security hole and roll out a configuration file pointing the loader to version 1.0.1.0 of your library assembly. The disconnected user knows none of this and attempts to run the customization again.

Look at this from the point of view of the CLR assembly loader: It has been asked to load a file off an unavailable Web server. It tries to find a local copy of the assembly, and it succeeds. It tries to find a local copy of the configuration file, but it fails. If you had cached a local copy of the configuration file, the CLR can assume that you meant for it to use that configuration file and that you were fine with using potentially out-of-date configuration information. But because there is no cached file, the CLR has to assume the worst: that there is, in fact, a new and important configuration file available that it cannot find.

Therefore, if you want to ensure that users must always be online and using the latest version of your server-side customization, you should not create a configuration file on the server. On the other hand, if you want to allow users to use cached assemblies and configuration files when your server is inaccessible, ensure that you have a configuration file on the server.

To add a configuration file to your project, right-click the project in Solution Explorer, and choose Add > New Item > Application Configuration File. Name the configuration file after the customization assembly filename. If your assembly is ExpenseReport.dll, for example, name the configuration file ExpenseReport.dll.config.

The configuration file need not have any loading policies in it. For now, stick with the bare minimum:

<?xml version="1.0" encoding="utf-8" ?> <configuration> </configuration> 


Configuration files do not get any more straightforward than that. To ensure that this file is copied up to the deployment server, make sure that the Build Action is set to Content in the Properties pane for the configuration file.





Visual Studio Tools for Office(c) Using Visual Basic 2005 with Excel, Word, Outlook, and InfoPath
Visual Studio Tools for Office: Using Visual Basic 2005 with Excel, Word, Outlook, and InfoPath
ISBN: 0321411757
EAN: 2147483647
Year: N/A
Pages: 221

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net