2013-04-10 71 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> 
相關問題