使用MSI安装程序版本设置程序集信息版本号

19
我正在使用安装程序项目来发布我的项目。我希望每个项目的版本与安装程序版本相同。
我想在Visual Studio中更改我的安装程序版本属性,并在构建后,使所有项目版本从此属性更新,这可能吗?

1
什么是“设置版本”? - Simon Mourier
@SimonMourier 那也曾经让我感到困惑,但是在我的回答中第二张截图中有展示。 - Jeremy Thompson
哦,据我所知,安装程序项目的“版本”属性在最终的MSI文件中并没有被写入,那这有什么意义呢? - Simon Mourier
我已经编辑了我的答案 - 希望它们涵盖了所有的基础知识。@SimonMourier FYI,“Version”属性是写入的,因为你可以从MSI程序中以编程方式获取版本。问题是这只能用于PostBuildEvent,它将使用最后一个版本而不是最新的版本。干杯 - Jeremy Thompson
为了澄清上面的评论,你需要构建两次。第二次构建中,assembly.cs文件将拥有最新版本。 - Jeremy Thompson
2个回答

45

项目有程序集和文件版本号:(不是安装版本,我已经相应地编辑了您的问题)

enter image description here

答案1:

如果您想使安装程序的版本号设置为程序集和文件版本号,则需要使用脚本/ exe来触发构建。

enter image description here

这篇关于如何自动更新程序集版本号的文章展示了其中一半的解决方案...
从我的研究来看,在PreBuildEvent中无法使用SetupVersion。它没有$SetupVersion命令: http://msdn.microsoft.com/en-us/library/42x5kfw4(v=vs.80).aspx 每次构建都需要更改PreBuildEvent,如Code Project文章中此评论所示,使用-set:命令并不理想。
我们需要的解决方案是一个PreBuildEvent调用AssemblyInfoUtil.exe并从vdproj项目文件中读取“ProductVersion”,然后更新程序集版本号。

我已经修改了这篇文章的代码,来展示如何从Setup.vdproj中读取产品版本,并且可以通过PreBuildEvent调用:

AssemblyInfoUtil.exe -setup:"C:\Program Files\MyProject1\Setup1\Setup1.vdproj" -ass:"C:\Program Files\MyProject1\AssemblyInfo.cs"

这是修改后的代码:
using System;
using System.IO;
using System.Text;

namespace AssemblyInfoUtil
{
    class AssemblyInfoUtil
    {
    private static int incParamNum = 0;    
    private static string fileName = "";  
    private static string setupfileName = "";       
    private static string versionStr = null;    
    private static bool isVB = false;
    [STAThread]
    static void Main(string[] args)
    {
        for (int i = 0; i < args.Length; i++) {
            if (args[i].StartsWith("-setup:")) {
           string s = args[i].Substring("-setup:".Length);
           setupfileName = int.Parse(s);
            }
            else if (args[i].StartsWith("-ass:")) {
           fileName = args[i].Substring("-ass:".Length);
            }
        }

        //Jeremy Thompson showing how to detect "ProductVersion" = "8:1.0.0" in vdproj
        string setupproj = System.IO.File.ReadAllText(setupfileName);
        int startPosOfProductVersion = setupproj.IndexOf("\"ProductVersion\" = \"") +20;
        int endPosOfProductVersion = setupproj.IndexOf(Environment.NewLine, startPosOfProductVersion) - startPosOfProductVersion;
        string versionStr = setupproj.Substring(startPosOfProductVersion, endPosOfProductVersion);
        versionStr = versionStr.Replace("\"", string.Empty).Replace("8:",string.Empty);

        if (Path.GetExtension(fileName).ToLower() == ".vb")
        isVB = true;

        if (fileName == "") {
        System.Console.WriteLine("Usage: AssemblyInfoUtil 
           <path to :Setup.vdproj file> and  <path to AssemblyInfo.cs or AssemblyInfo.vb file> [options]");
        System.Console.WriteLine("Options: ");
        System.Console.WriteLine("  -setup:Setup.vdproj file path");
        System.Console.WriteLine("  -ass:Assembly file path");
        return;
        }

        if (!File.Exists(fileName)) {
        System.Console.WriteLine
            ("Error: Can not find file \"" + fileName + "\"");
        return;
        }

        System.Console.Write("Processing \"" + fileName + "\"...");
        StreamReader reader = new StreamReader(fileName);
             StreamWriter writer = new StreamWriter(fileName + ".out");
        String line;

        while ((line = reader.ReadLine()) != null) {
        line = ProcessLine(line);
        writer.WriteLine(line);
        }
        reader.Close();
        writer.Close();

        File.Delete(fileName);
        File.Move(fileName + ".out", fileName);
        System.Console.WriteLine("Done!");
    }

    private static string ProcessLine(string line) {
        if (isVB) {
        line = ProcessLinePart(line, "<Assembly: AssemblyVersion(\"");
        line = ProcessLinePart(line, "<Assembly: AssemblyFileVersion(\"");
        } 
        else {
        line = ProcessLinePart(line, "[assembly: AssemblyVersion(\"");
        line = ProcessLinePart(line, "[assembly: AssemblyFileVersion(\"");
        }
        return line;
    }

    private static string ProcessLinePart(string line, string part) {
        int spos = line.IndexOf(part);
        if (spos >= 0) {
        spos += part.Length;
        int epos = line.IndexOf('"', spos);
        string oldVersion = line.Substring(spos, epos - spos);
        string newVersion = "";
        bool performChange = false;

        if (incParamNum > 0) {
            string[] nums = oldVersion.Split('.');
            if (nums.Length >= incParamNum && nums[incParamNum - 1] != "*") {
            Int64 val = Int64.Parse(nums[incParamNum - 1]);
            val++;
            nums[incParamNum - 1] = val.ToString();
            newVersion = nums[0]; 
            for (int i = 1; i < nums.Length; i++) {
                newVersion += "." + nums[i];
            }
            performChange = true;
            }
        }

        else if (versionStr != null) {
            newVersion = versionStr;
            performChange = true;
        }

        if (performChange) {
            StringBuilder str = new StringBuilder(line);
            str.Remove(spos, epos - spos);
            str.Insert(spos, newVersion);
            line = str.ToString();
        }
        } 
        return line;
    }
     }
}

答案2:

在我看来,更好的方法是使用Shared Assembly Info类而不是单独的AssemblyInfo类文件。

要实现这一点,在解决方案文件夹中创建一个名为SharedAssemblyInfo.cs的文件,然后在每个项目中添加一个到SharedAssemblyInfo.cs的链接。您还可以将链接的SharedAssemblyInfo.cs移动到Properties文件夹中,以便它与解决方案中每个项目特定的AssemblyInfo.cs并排放置,如下所示。

enter image description here

这是一个示例SharedAssemblyInfo.cs文件:
using System;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

// General Information about an assembly is controlled through the following 
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyCompany("Saint Bart Technologies")]
[assembly: AssemblyProduct("Demo")]
[assembly: AssemblyCopyright("Copyright ? Saint Bart 2013")]
[assembly: AssemblyTrademark("")]

// Make it easy to distinguish Debug and Release (i.e. Retail) builds;
// for example, through the file properties window.
#if DEBUG
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyDescription("Flavor=Debug")] // a.k.a. "Comments"
#else
[assembly: AssemblyConfiguration("Retail")]
[assembly: AssemblyDescription("Flavor=Retail")] // a.k.a. "Comments"
#endif

[assembly: CLSCompliant(true)]

// Setting ComVisible to false makes the types in this assembly not visible 
// to COM components.  If you need to access a type in this assembly from 
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]

// Note that the assembly version does not get incremented for every build
// to avoid problems with assembly binding (or requiring a policy or
// <bindingRedirect> in the config file).
//
// The AssemblyFileVersionAttribute is incremented with every build in order
// to distinguish one build from another. AssemblyFileVersion is specified
// in AssemblyVersionInfo.cs so that it can be easily incremented by the
// automated build process.
[assembly: AssemblyVersion("1.0.0.0")]

// By default, the "Product version" shown in the file properties window is
// the same as the value specified for AssemblyFileVersionAttribute.
// Set AssemblyInformationalVersionAttribute to be the same as
// AssemblyVersionAttribute so that the "Product version" in the file
// properties window matches the version displayed in the GAC shell extension.
[assembly: AssemblyInformationalVersion("1.0.0.0")] // a.k.a. "Product version"

以下是一个AssemblyInfo.cs文件的示例:
// Note: Shared assembly information is specified in SharedAssemblyInfo.cs
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following 
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("WindowsFormsApplication2")]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("ffded14d-6c95-440b-a45d-e1f502476539")]

每次想要更改所有项目的程序集信息时,您可以在一个地方完成。我假设您希望将MSI设置版本设置为程序集版本号,这需要手动操作一次。


答案 3:

考虑切换使用MSBuild,它具有所有这些好处,但我不确定你现在是否有时间学习。


答案4:

使用以下星号语法,AssemblyInfo.cs中的程序集可以自动递增其构建号码:

[assembly: AssemblyVersion("1.0.0.*")]

这是一个好方法,因为跟踪构建号的目的是能够识别不同的构建版本。在预构建更改构建号会破坏此目的,因为构建尚未发生。


答案5:

这里的另一个CodeProject答案假设您想要在设置MSI项目文件中更新ProductVersion、ProductCode、PackageCode。我没有这样解释你的问题,根据这个线程,有问题: pre-build event to change setup project's ProductVersion doesn't take effect until after the build

答案6(新):

以下是相关的 TFS Build 插件用于设置 "Assembly Info":https://marketplace.visualstudio.com/items?itemName=bleddynrichards.Assembly-Info-Task https://marketplace.visualstudio.com/items?itemName=bool.update-assembly-info https://marketplace.visualstudio.com/items?itemName=ggarbuglia.setassemblyversion-task。请注意,不要删除 HTML 标签。

很好的回答,但我在答案1的代码示例中发现了一些问题。没有必要将args解析为整数以用于SetupfileName(编译错误),而versionStr不应该被重新声明为本地变量(使脚本无法执行且没有任何错误!)。 - Marco Guignard
#2能够与VS安装项目一起工作吗?我无法找到一种方法使安装项目将.cs文件识别为自己的程序集,而不是要安装的内容。 - Ian
@Ian,它不是这样工作的,你要跟踪构建版本而不是用于构建的系统。 - Jeremy Thompson
谢谢@Jeremy,恐怕我不明白。我试图让我的应用程序和安装程序项目的“构建”版本保持同步(根据OP)。解决方案#2看起来最简单、最整洁,但我找不到一种方法使其与安装程序项目一起工作(该项目没有AssemblyInfo.cs)。 - Ian
@Ian - 也许如果安装程序项目是解决方案的一部分,它会以那种方式工作。问题在于您需要更改各自项目中的AssemblyInfo.cs。安装程序没有它,因为它不是DLL或EXE。它是一个构建项目。因此,您希望跟踪构建情况。#2解决方案允许所有项目共享相同的“AssemblyInfo.cs”,类似于#1解决方案。我认为您希望有一个构建项目可以获取该信息。 - Jeremy Thompson
这是姊妹问题和答案:https://dev59.com/V2bWa4cB1Zd3GeqPTBaV - Jeremy Thompson

2

我不知道这是否完美地解决了你的问题,但是你可以实现一个包含所有配置管理信息的公共类,例如:

public class VersionInfo{
    public const string cProductVersion = "1.0.0"
    //other version info
}

在你使用新的类之后,你可以更新所有的AssemblyInfo.cs:
[assembly: AssemblyVersion(VersionInfo.cProductVersion)]

我希望这可以帮到你。

请查看我的第二个答案,那是官方的方法,你尝试得不错。 - Jeremy Thompson
谢谢你的提示,我会在你的解决方案之后更改我的项目设置。 - yaens

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接