2013-04-10 69 views
3

我在Visual Studio 2010解决方案中有许多Visual C++项目。在这个解决方案中还有一个WiX项目,它为C++项目之一的产品构建可执行文件的安装程序。在MSBuild中获取本机exe的文件版本

可执行文件的项目中有一个资源文件,该文件将程序的版本写入可执行文件。

现在我想用与由资源文件写入可执行文件的编号相同的编号来编译WiX构建的安装程序。我搜索StackOverflow上的WiX的相关帖子,发现这个帖子:

Referencing a WixVariable defined in a WiX Library Project from a WiX Setup Project

接受的答案到,似乎表明一个可能的解决方案是使用的MSBuild和GetAssemblyIdentity任务在BeforeBuild目标从另一个文件中获取版本号(在SO问题中为DLL,在我的情况下为可执行文件)并在WiX构建安装程序之前将其公开给WiX。

我尝试添加这对我.wixproj文件的MSBuild的一部分,但是当我试图生成安装程序我得到一个错误回话说:

error MSB3441: Cannot get assembly name for "<ExePath>". Could not load file or assembly '<ExeName>.exe' or one of its dependencies. The module was expected to contain an assembly manifest. 

我似乎无法找到MSDN的任何信息关于这个错误,因为它涉及MSBuild。我已经检查过内置的可执行文件,它的版本号(以及.rc文件中的其他信息)以及WiX项目依赖于输出可执行文件的项目;所以我假设它的BeforeBuild任务在它所依赖的项目完全构建完成后运行。

我是否应该使用不同的任务而不是GetAssemblyIdentity从MSBuild中的.exe中检索版本号,在GetAssemblyIdentity工作之前是否还有其他需求需要满足,还是仅仅是无法获得有关此类信息的信息MSBuild中的.exe文件?

编辑:

我接受Rob的答案,因为我是误解的ProductVersion和文件版本,以及WiX的技术,他建议,正在按原计划,是对我所需要的溶液的步骤之间的差异。

FileVersion只是可执行文件的一个属性。 Msi文件本质上是数据库,而ProductVersion是该数据库中的一个条目;他们没有FileVersion属性来设置。他建议的方法在.msi数据库中正确设置了ProductVersion。

这个问题的标题现在并不真正与我实际存在的问题有关,因为我当时正在寻求一种我认为我当时需要的解决方案。我现在已经解决了只能访问安装程序的ProductVersion的根本问题。我在这里发现了一个cscript脚本:http://kentie.net/article/wixnameversion/index.htm显示了如何访问.msi的ProductVersion。使用它可以让我提取ProductVersion并在其他工具中使用它。

+0

会使用预处理器扩展来抓取文件版本是不可能的? – Netfangled 2013-04-10 20:57:35

+0

您使用的是什么版本的WiX工具集? – 2013-04-10 23:27:36

+0

我使用的是版本3.7 – Alex 2013-04-11 00:18:56

回答

7

如果您不需要MSBuild中的版本,更简单的解决方案是直接在您的.wxs文件中引用该文件的版本。下面是显示做一个片段:

<?xml version="1.0" encoding="UTF-8"?> 
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"> 
    <Product Version="!(bind.fileVersion.ExeWithVersion)" ...> 

    ... 

    <Component ...> 
    <File Id="ExeWithVersion" Source="path\to\your\versioned\file.exe" /> 
    </Component> 

    ... 

    </Product> 
</Wix> 

神奇的是,在!(bind.fileVersion.Xxx)说来查找File元素与Id='Xxx',并得到其版本。这是将文件的版本放入MSI软件包的最简单方法。

+0

这是我第一次尝试,但不幸的是,由于我链接到的StackOverflow问题解释说,尽管WiX不会对此抱怨,但似乎并不奏效。我用这种方法构建了我的安装程序,但没有可用的版本信息。 – Alex 2013-04-10 21:44:14

+0

我在很多情况下都用过这个。有一件事要检查,你是更新文件版本还是程序集版本?如果程序集版本将'!(bind.fileVersion.Xxx)'更改为'!(bind.assemblyFileVersion.Xxx)'。 – 2013-04-10 22:07:47

+0

我试着将它改为assemblyFileVersion来检查刚才,但是然后WiX无法理解!(bind.assemblyFileVersion.Xxx),所以我不认为我设置了该版本号。作为一个测试,我尝试在产品的制造商字段中使用!(bind.fileVersion.Xxx),并在安装程序上显示正确的版本号。所以它似乎是产品版本字符串特有的问题。它似乎可以解决!(bind.fileVersion.Xxx),但将其放入版本字段似乎不起作用。你可以从它为你工作的地方发布一些示例代码片段吗?这可能有帮助。 – Alex 2013-04-10 22:47:18

2

我需要一次文件版本,最后我写了一个自定义任务来获取FileVersion,因为我找不到任何东西。

namespace GranadaCoder.Framework.CrossDomain.MSBuild.Tasks.IO//.FileVersionTask 
{ 
    using System; 
    using System.Collections.Generic; 
    using System.Collections; 
    using System.Linq; 
    using System.Globalization; 
    using System.Diagnostics; 
    using System.IO; 
    using System.Text; 
    using System.Security; 

    using Microsoft.Build.Framework; 
    using Microsoft.Build.Utilities; 

    public class FileVersionTask : FileBasedTaskBase 
    { 
     private static readonly string ROOT_DIRECTORY = "myrootdir"; 
     private static readonly string FULL_PATH = "myfullpath"; 
     private static readonly string FILE_NAME = "myfilename"; 
     private static readonly string DIRECTORY = "mydirectory"; 
     private static readonly string EXTENSION = "myextension"; 
     private static readonly string VERSION = "myfileversion"; 

     /// <summary> 
     /// Gets or sets the source files. 
     /// </summary> 
     /// <value>The source files.</value> 
     [Required] 
     public string SourceFiles { get; set; } 

     /// <summary> 
     /// Gets the file versions as a Task Output property. 
     /// </summary> 
     /// <value>The file versions.</value> 
     [Output] 
     public ITaskItem[] FileVersions 
     { get; private set; } 

     /// <summary> 
     /// Task Entry Point. 
     /// </summary> 
     /// <returns></returns> 
     protected override bool AbstractExecute() 
     { 
      InternalExecute(); 
      return !Log.HasLoggedErrors; 
     } 

     /// <summary> 
     /// Internal Execute Wrapper. 
     /// </summary> 
     private void InternalExecute() 
     { 
      IList<string> files = null; 

      if (String.IsNullOrEmpty(this.SourceFiles)) 
      { 
       Log.LogWarning("No SourceFiles specified"); 
       return; 
      } 

      if (!String.IsNullOrEmpty(this.SourceFiles)) 
      { 
       Console.WriteLine(this.SourceFiles); 
       files = base.ConvertSourceFileStringToList(this.SourceFiles); 
      } 

      //List<string> fileVersions = new List<string>(); 

      ArrayList itemsAsStringArray = new ArrayList(); 

      foreach (string f in files) 
      { 
       FileInfoWrapper fiw = null; 
       fiw = this.DetermineFileVersion(f); 

       IDictionary currentMetaData = new System.Collections.Hashtable(); 

       currentMetaData.Add(ROOT_DIRECTORY, fiw.RootDirectory); 
       currentMetaData.Add(FULL_PATH, fiw.FullPath); 
       currentMetaData.Add(FILE_NAME, fiw.FileName); 
       currentMetaData.Add(DIRECTORY, fiw.Directory); 
       currentMetaData.Add(EXTENSION, fiw.Extension); 
       currentMetaData.Add(VERSION, fiw.Version); 

       itemsAsStringArray.Add(new TaskItem(fiw.Version, currentMetaData)); 

      } 
      this.FileVersions = (ITaskItem[])itemsAsStringArray.ToArray(typeof(ITaskItem)); 
     } 


     /// <summary> 
     /// Determines the file version. 
     /// </summary> 
     /// <param name="fileName">Name of the file.</param> 
     /// <returns>File version or 0.0.0.0 if value cannot be determined</returns> 
     private FileInfoWrapper DetermineFileVersion(string fileName) 
     { 
      FileInfoWrapper fiw = new FileInfoWrapper(); 
      fiw.Directory = string.Empty; 
      fiw.Extension = string.Empty; 
      fiw.FileName = string.Empty; 
      fiw.FullPath = string.Empty; 
      fiw.RootDirectory = string.Empty; 
      fiw.Version = "0.0.0.0"; 
      try 
      { 
       if (System.IO.File.Exists(fileName)) 
       { 
        fiw.Extension = System.IO.Path.GetExtension(fileName); 
        fiw.FileName = System.IO.Path.GetFileNameWithoutExtension(fileName); 
        fiw.FullPath = fileName;// System.IO.Path.GetFileName(fileName); 
        fiw.RootDirectory = System.IO.Path.GetPathRoot(fileName); 

        //Take the full path and remove the root directory to mimic the DotNet default behavior of '%filename' 
        fiw.Directory = System.IO.Path.GetDirectoryName(fileName).Remove(0, fiw.RootDirectory.Length); 

        FileVersionInfo fvi = FileVersionInfo.GetVersionInfo(fileName); 
        if (null != fvi) 
        { 
         if (null != fvi.FileVersion) 
         { 
          fiw.Version = fvi.FileVersion; 
         } 
        } 
       } 
      } 
      catch (Exception ex) 
      { 
       if (ex is IOException 
        || ex is UnauthorizedAccessException 
        || ex is PathTooLongException 
        || ex is DirectoryNotFoundException 
        || ex is SecurityException) 
       { 
        Log.LogWarning("Error trying to determine file version " + fileName + ". " + ex.Message); 
       } 
       else 
       { 
        Log.LogErrorFromException(ex); 
        throw; 
       } 
      } 
      return fiw; 
     } 




     /// <summary> 
     /// Internal wrapper class to hold file properties of interest. 
     /// </summary> 
     internal sealed class FileInfoWrapper 
     { 
      public string Directory { get; set; } 
      public string Extension { get; set; } 
      public string FileName { get; set; } 
      public string FullPath { get; set; } 
      public string RootDirectory { get; set; } 
      public string Version { get; set; } 
     } 
    } 
} 

。msbuild示例

<?xml version="1.0" encoding="utf-8"?> 
<Project DefaultTargets="AllTargetsWrapper" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> 

    <UsingTask AssemblyFile="GranadaCoder.Framework.CrossDomain.MSBuild.dll" TaskName="FileVersionTask"/> 




    <Target Name="AllTargetsWrapper"> 
    <CallTarget Targets="FileVersionTask1" /> 
    <CallTarget Targets="FileVersionTask2" /> 
    </Target> 


    <PropertyGroup> 
    <WorkingCheckout>c:\Program Files\MSBuild</WorkingCheckout> 
    </PropertyGroup> 


    <ItemGroup> 
    <MyTask1ExcludeFiles Include="$(WorkingCheckout)\**\*.rtf" /> 
    <MyTask1ExcludeFiles Include="$(WorkingCheckout)\**\*.doc" /> 
    </ItemGroup> 

    <ItemGroup> 
    <MyTask1IncludeFiles Include="$(WorkingCheckout)\**\*.exe" Exclude="@(MyTask1ExcludeFiles)" /> 
    </ItemGroup> 

    <Target Name="FileVersionTask1"> 
    <FileVersionTask SourceFiles="@(MyTask1IncludeFiles)" > 

     <Output TaskParameter="FileVersions" ItemName="MyFileVersionItemNames"/> 

    </FileVersionTask> 


    <Message Text=" MyFileVersionItemNames MetaData "/> 
    <Message Text=" ------------------------------- "/> 
    <Message Text=" "/> 


    <Message Text="directory: "/> 
    <Message Text="@(MyFileVersionItemNames->'%(mydirectory)')"/> 
    <Message Text=" "/> 
    <Message Text=" "/> 
    <Message Text="extension: "/> 
    <Message Text="@(MyFileVersionItemNames->'%(myextension)')"/> 
    <Message Text=" "/> 
    <Message Text=" "/> 
    <Message Text="filename: "/> 
    <Message Text="@(MyFileVersionItemNames->'%(myfilename)')"/> 
    <Message Text=" "/> 
    <Message Text=" "/> 
    <Message Text="fullpath: "/> 
    <Message Text="@(MyFileVersionItemNames->'%(myfullpath)')"/> 
    <Message Text=" "/> 
    <Message Text=" "/> 
    <Message Text="rootdir: "/> 
    <Message Text="@(MyFileVersionItemNames->'%(myrootdir)')"/> 
    <Message Text=" "/> 
    <Message Text=" "/> 

    <Message Text="fileversion: "/> 
    <Message Text="@(MyFileVersionItemNames->'%(myfileversion)')"/> 
    <Message Text=" "/> 
    <Message Text=" "/> 


    <Message Text=" "/> 
    <Message Text=" "/> 
    <Message Text="rootdir + directory + filename + extension: "/> 
    <Message Text="@(MyFileVersionItemNames->'%(myrootdir)%(mydirectory)%(myfilename)%(myextension)')"/> 
    <Message Text=" "/> 
    <Message Text=" "/> 



    <Message Text="List of files using special characters (carriage return)"/> 
    <Message Text="@(MyFileVersionItemNames->'&quot;%(myfullpath)&quot;' , '%0D%0A')"/> 
    <Message Text=" "/> 
    <Message Text=" "/> 



    </Target> 



    <ItemGroup> 
    <MyTask2IncludeFiles Include="c:\windows\notepad.exe" /> 
    </ItemGroup> 

    <Target Name="FileVersionTask2"> 
    <FileVersionTask SourceFiles="@(MyTask2IncludeFiles)" > 
     <Output TaskParameter="FileVersions" PropertyName="SingleFileFileVersion"/> 
    </FileVersionTask> 

    <Message Text="SingleFileFileVersion = $(SingleFileFileVersion) "/> 

    </Target> 


</Project> 
相关问题