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

问题描述 投票:7回答:2

我正在创建一个用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()

我意识到这是由于.VSTO.manifest.DLL文件没有被正确引用,因为Excel电子表格不再存在于已安装的路径中。做some research之后,我可以通过更改custom.xml文件中复制/保存的Excels .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"

无济于事。

c# installer vsto
2个回答
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项目的任何更改重建为发布版本,因为它指向的是发行版,而不是调试版。


0
投票

虽然James Mertz的回答很明显,但它也有点过时了(虽然它只是在我拔掉头发一天后救了我)。对于较新版本的Visual Studios,应用程序构建的这一部分的说明已移至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“Application Files”

然后转到Install Shield中的“Custom Action”并双击“安装期间的自定义操作”部分中的“安装后成功完成对话框”。在安装完成后,您可以在此处运行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是一个不错的选择)。

© www.soinside.com 2019 - 2024. All rights reserved.