7

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

哦哦!

包含以下完整详细信息:

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

我意识到这是由于Excel 电子表格不再驻留在安装路径中,因此没有正确引用to.VSTO.manifest&文件。.DLL在做了一些研究之后,我可以通过更改文件中复制/保存的 Excelscustom.xml文件来手动解决这个.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# 代码或(更优选)安装程序进行上述更改?

注意:我尝试创建自定义安装操作(根据此 MSDN 教程),其中设置了以下内容CustomActionData

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

无济于事。

4

2 回答 2

8

您需要按照您引用的MSDN 文章中概述的说明进行操作,再多一点。但是,这有点令人困惑,并且文章中有错误。希望这将有助于澄清:

您需要定义类似于文章提供的用户脚本

在文章中,您可以下载一个包含项目示例的文件。从那里你可以参考你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"

但是应该是这样的(注意参数之间的空格):

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

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

于 2012-07-18T21:50:46.843 回答
0

虽然 James Mertz 的回答是正确的,但它也有点过时(尽管它只是在拔掉我的头发一天后救了我)。对于较新版本的 Visual Studio,应用程序构建的这一部分的说明已移至此处。我建议你去阅读它们,或者你可以阅读下面我的悬崖笔记版本:

应在现有解决方案中作为新项目构建的新控制台应用程序。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 Setup 向其传递参数。

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

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

现在在安装完成后调用这个新应用程序并将其传递参数(也在该指令链接中):将该新项目的主要输出添加到您的 Install Shield“应用程序文件”

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

在您的新自定义操作中,在Source Location属性的列表中,选择“Installed with the Product”并浏览到您的新项目主输出文件,您的 VSTO 应用程序将安装在目标上。

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

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

必须为您的 dll、vsto、工作簿和解决方案 id 更改这些内容。解决方案 ID 可以在您的.csproj文件中找到(在文件资源管理器中找到它并在记事本中打开它)。

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

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

于 2019-02-21T23:12:31.457 回答