如何分配Excel VSTO安装的程序集位置?

时间:2012-07-18 06:47:14

标签: c# installer vsto

我正在创建一个用C#编写的文档级工作簿/模板,并使用VSTO安装程序来部署代码。安装项目后我有完整的电子表格功能,但是当我将安装的工作表保存或复制到安装文件夹之外的另一个路径时,我收到以下错误:

Uh Oh!

以下详细信息:

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()

我意识到这应归功于.VSTO.manifest&由于Excel电子表格不再位于已安装的路径中,因此未正确引用.DLL个文件。执行some research后,我可以通过更改custom.xml文件中的复制/保存的Excel .xlsx文件来手动修复此问题:

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

对此:

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

由于这对我的客户来说不是一个可行的解决方案,如何使用C#代码或(更优选)安装程序进行上述更改?

注意:我已尝试创建自定义安装操作(per this MSDN Tutorial),其中为CustomActionData设置了以下内容:

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

无济于事。

2 个答案:

答案 0 :(得分:8)

您需要按照您引用的MSDN article中列出的说明进行操作,再多一点。然而,这有点令人困惑,文章中有错误。希望这有助于澄清:

您需要定义类似于文章

提供的用户脚本

在文章中有一个文件,您可以download包含项目示例。从那里你可以引用你的Custom Actions来反对那个cs项目的输出。创建一个CS类库的新项目,复制以下用于解决您的问题的用户脚本:

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);
        }

    }
}

这将覆盖安装程序的安装过程。 base.Install(stateSaver)调用其余代码以正常继续安装。

MSDN文章中的错误:

文章说使用以下内容来安装自定义操作的CustomActionData

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

然而它应该是这个(注意params之间的空格):

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

这应该可以解决您的问题,但是在重建安装程序之前,请确保将Excel项目的任何更改重建为发布版本,因为它指向的是发行版,而不是调试版。

答案 1 :(得分:0)

尽管詹姆斯·默茨(James Mertz)的答案很明确,但它也有些过时(尽管在整整一天的拔头发之后才救了我)。对于较新版本的Visual Studio,该应用程序构建此部分的说明已移至here。我建议您去阅读它们,或者您可以在下面阅读我的悬崖笔记版本:

应该在现有解决方案中作为新项目构建的新控制台应用程序。 Program.cs应该看起来像:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.VisualStudio.Tools.Applications.Runtime;
using Microsoft.VisualStudio.Tools.Applications;

namespace SetExcelDocumentProperties
{
    class Program
    {
        static void Main(string[] args)
        {
            string assemblyLocation = "";
            Guid solutionID = new Guid();
            Uri deploymentManifestLocation = null;
            string documentLocation = "";
            string[] nonpublicCachedDataMembers = null;

            for (int i = 0; i <= args.Count() - 1; i++)
            {
                Console.WriteLine(args[i]);
                string[] oArugment = args[i].Split('=');

                switch (oArugment[0])
                {
                    case "/assemblyLocation":
                        assemblyLocation = oArugment[1];
                        break;
                    case "/deploymentManifestLocation":
                        if (!Uri.TryCreate(oArugment[1], UriKind.Absolute, out deploymentManifestLocation))
                        {
                            Console.WriteLine("Error creating URI");
                        }
                        break;
                    case "/documentLocation":
                        documentLocation = oArugment[1];
                        break;
                    case "/solutionID":
                        solutionID = Guid.Parse(oArugment[1]);
                        break;
                }
            }
            try
            {
                ServerDocument.RemoveCustomization(documentLocation);
                ServerDocument.AddCustomization(documentLocation, assemblyLocation,
                                            solutionID, deploymentManifestLocation,
                                            true, out nonpublicCachedDataMembers);

            }
            catch (System.IO.FileNotFoundException)
            {
                Console.WriteLine("The specified document does not exist.");
            }
            catch (System.IO.IOException)
            {
                Console.WriteLine("The specified document is read-only.");
            }
            catch (InvalidOperationException ex)
            {
                Console.WriteLine("The customization could not be removed.\n" +
                    ex.Message);
            }
            catch (DocumentNotCustomizedException ex)
            {
                Console.WriteLine("The document could not be customized.\n" +
                    ex.Message);
            }
        }
    }
}

由于我们将通过InstallShield设置将参数传递给它,因此无需更改任何内容。

这在新项目中需要两个引用:

  • Microsoft.VisualStudio.Tools.Applications.Runtime
  • Microsoft.VisualStudio.Tools.Applications.ServerDocument

现在安装完成后立即调用此新应用程序并传递参数( 也在该说明链接中 ):将新项目的主要输出添加到您的安装中屏蔽“应用程序文件”

然后转到Install Shield中的“自定义操作” ,然后双击“安装期间的自定义操作”中的“安装完成后成功对话框” < / em>部分。安装完成后,您可以在此处运行vbscript,jscript或可执行文件。在这里,我们将运行新的控制台应用程序可执行文件,并为其传递参数。

在新的自定义操作中,在源位置属性的列表中,选择“产品已安装” 并浏览到新项目主要输出文件,将VSTO应用程序安装到目标位置。

现在在“命令行” 属性框中,放置控制台应用程序需要运行的参数列表:

/assemblyLocation="[INSTALLDIR]ExcelWorkbook.dll" /deploymentManifestLocation="[INSTALLDIR]ExcelWorkbook.vsto" /documentLocation="[INSTALLDIR]ExcelWorkbook.xlsx" /solutionID="Your Solution ID"

必须更改您的dll,vsto,工作簿和解决方案ID 。可以在您的.csproj文件内部找到解决方案ID(在文件浏览器中,找到它并在记事本中打开它)。

现在你很黄金了。应用程序安装成功后,将启动此控制台应用程序,它将您的工作簿的manifestLocation更改为安装路径,而不是当前工作簿的相对路径。

如果要安装的人员没有计算机管理员权限,并且您要安装到Program Files目录中,则可能会发现在执行此步骤之前,他们已经可以安装和运行,但是之后就无法安装和运行。这是因为工作簿在该位置是只读的。在这种情况下,您必须选择另一个位置来安装应用程序(AppDataLocal是一个不错的选择)。