Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you assign the Assembly Location of an Excel VSTO Installation?

I'm creating a Document-Level Workbook/Template written in C# and using a VSTO Installer to deploy the code. After installing the project I have full functionality of the Spreadsheet however, when I save or copy the installed sheet to another path outside the installation folder I get the following error:

Uh Oh!

With the following full details:

Name: 
From: file:///C:/Users/Kronos/Desktop/ExcelTemplate1.vsto

************** Exception Text **************
System.Deployment.Application.DeploymentDownloadException: Downloading file:///C:/Users/Kronos/Desktop/ExcelTemplate1.vsto did not succeed. ---> System.Net.WebException: Could not find file 'C:\Users\Kronos\Desktop\ExcelTemplate1.vsto'. ---> System.Net.WebException: Could not find file 'C:\Users\Kronos\Desktop\ExcelTemplate1.vsto'. ---> System.IO.FileNotFoundException: Could not find file 'C:\Users\Kronos\Desktop\ExcelTemplate1.vsto'.
   at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
   at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)
   at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options, String msgPath, Boolean bFromProxy)
   at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, Boolean useAsync)
   at System.Net.FileWebStream..ctor(FileWebRequest request, String path, FileMode mode, FileAccess access, FileShare sharing, Int32 length, Boolean async)
   at System.Net.FileWebResponse..ctor(FileWebRequest request, Uri uri, FileAccess access, Boolean asyncHint)
   --- End of inner exception stack trace ---
   at System.Net.FileWebResponse..ctor(FileWebRequest request, Uri uri, FileAccess access, Boolean asyncHint)
   at System.Net.FileWebRequest.GetResponseCallback(Object state)
   --- End of inner exception stack trace ---
   at System.Net.FileWebRequest.EndGetResponse(IAsyncResult asyncResult)
   at System.Net.FileWebRequest.GetResponse()
   at System.Deployment.Application.SystemNetDownloader.DownloadSingleFile(DownloadQueueItem next)
   --- End of inner exception stack trace ---
   at Microsoft.VisualStudio.Tools.Applications.Deployment.ClickOnceAddInDeploymentManager.GetManifests(TimeSpan timeout)
   at Microsoft.VisualStudio.Tools.Applications.Deployment.ClickOnceAddInDeploymentManager.InstallAddIn()

I realize that this is due the to .VSTO, .manifest & .DLL files not being properly referenced as the Excel spreadsheet no longer resides in the installed path. After doing some research I can manually fix this by changing the copied/saved Excels custom.xml file within the .xlsx file from this:

name="_AssemblyLocation"><vt:lpwstr>ExcelTemplate1.vsto|ca022788-e7c0-41d8-b8ae-2c0ba9edbbf8|vstolocal

To this:

name="_AssemblyLocation"><vt:lpwstr>file://c:/<path to install dir>/ExcelTemplate1.vsto|ca022788-e7c0-41d8-b8ae-2c0ba9edbbf8|vstolocal

As this isn't a viable solution for my customers, how do I make the above changes using either C# code, or (more preferably) the installer?

Note: I've tried creating a Custom Install Action (per this MSDN Tutorial) where the following is set for CustomActionData:

/assemblyLocation="[TARGETDIR]ExcelWorkbookProject.dll"/deploymentManifestLocation="[TARGETDIR]ExcelWorkbookProject.vsto"/documentLocation="[TARGETDIR]ExcelWorkbookProject.xlsx"

to no avail.

like image 574
James Mertz Avatar asked Jul 18 '12 06:07

James Mertz


1 Answers

You need to follow the instructions outlined in the MSDN article you referenced, just a little more. However this is a little bit confusing and there is an error in the article. Hopefully this will help clarify:

You NEED to define a user script similar to what is provided by the article

Within the article there is a file that you can download that contains the project example. From there you can reference your Custom Actions against the output of that cs project. Create a new project that is a CS Class Library, copy the following user script specific to solving YOUR issue:

using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration.Install;
using System.Linq;
using Microsoft.VisualStudio.Tools.Applications;
using Microsoft.VisualStudio.Tools.Applications.Runtime;
using System.IO;
using System.Windows.Forms;

namespace AddCustomizationCustomAction
{
    [RunInstaller(true)]
    public partial class AddCustomization : System.Configuration.Install.Installer
    {
        //Note: you'll have to get the Guid from your specific project in order for it to work.  The MSDN article show you how.
        static readonly Guid SolutionID = new Guid("20cb4d1d-3d14-43c9-93a8-7ebf98f50da5");

        public override void Install(IDictionary stateSaver)
        {
            string[] nonpublicCachedDataMembers = null;


            // Use the following for debugging during the install
            //string parameters = "Parameters in Context.Paramters:";
            //foreach (DictionaryEntry parameter in Context.Parameters)
            //{
            //    parameters = parameters + "\n" + parameter.Key + ":" + parameter.Value;
            //}

            //MessageBox.Show(parameters);

            //MessageBox.Show("total items in parameters: " + Context.Parameters.Count);
            //MessageBox.Show("Document Manifest Location:" + Context.Parameters["deploymentManifestLocation"]);

            Uri deploymentManifestLocation = null;
            if (Uri.TryCreate(
                Context.Parameters["deploymentManifestLocation"],
                UriKind.RelativeOrAbsolute,
                out deploymentManifestLocation) == false)
            {
                throw new InstallException(
                    "The location of the deployment manifest " +
                    "is missing or invalid.");
            }
            string documentLocation =
                Context.Parameters["documentLocation"];
            if (String.IsNullOrEmpty(documentLocation))
            {
                throw new InstallException(
                    "The location of the document is missing.");
            }
            string assemblyLocation =
                Context.Parameters["assemblyLocation"];
            if (String.IsNullOrEmpty(assemblyLocation))
            {
                throw new InstallException(
                    "The location of the assembly is missing.");
            }

            // use the following for debugging
            MessageBox.Show(documentLocation);

            if (ServerDocument.IsCustomized(documentLocation))
            {
                ServerDocument.RemoveCustomization(documentLocation);
            }
            ServerDocument.AddCustomization(
                documentLocation,
                assemblyLocation,
                SolutionID,
                deploymentManifestLocation,
                false,
                out nonpublicCachedDataMembers);
            stateSaver.Add("documentlocation", documentLocation);
            base.Install(stateSaver);
        }

        public override void Commit(IDictionary savedState)
        {
            base.Commit(savedState);
        }

        public override void Rollback(IDictionary savedState)
        {
            base.Rollback(savedState);
        }

        public override void Uninstall(IDictionary savedState)
        {
            base.Uninstall(savedState);
        }

    }
}

This overrides the install procedure of the installer. The base.Install(stateSaver) calls the rest of the code to continue the installation as normal.

The Error in the MSDN Article:

The article says to use the following for the CustomActionData of the Install Custom Action:

/assemblyLocation="[TARGETDIR]<YourProjectName>.dll"/deploymentManifestLocation="[TARGETDIR]<YourProjectName>.vsto"/documentLocation="[TARGETDIR]<YourProjectName>.xltx"

However it should be this (note the spaces between params):

/assemblyLocation="[TARGETDIR]<YourProjectName>.dll" /deploymentManifestLocation="[TARGETDIR]<YourProjectName>.vsto" /documentLocation="[TARGETDIR]<YourProejctName>.xltx"

This should solve your issues, however make sure to rebuild any changes to your Excel Project to the release build before rebuilding your installer as it's pointed to the release and not the debug.

like image 78
James Mertz Avatar answered Sep 30 '22 05:09

James Mertz