C# 在构建后事件期间确定程序集版本

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2243593/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-07 00:41:52  来源:igfitidea点击:

Determine assembly version during a post-build event

c#.netvisual-studiodeploymentvisual-studio-2005

提问by Winston Smith

Let's say I wanted to create a static text file which ships with each release. I want the file to be updated with the version number of the release (as specified in AssemblyInfo.cs), but I don't want to have to do this manually.

假设我想创建一个随每个版本一起提供的静态文本文件。我希望使用发行版的版本号(如 中指定的AssemblyInfo.cs)更新文件,但我不想手动执行此操作。

I was hoping I could use a post-build event and feed the version number to a batch file like this:

我希望我可以使用构建后事件并将版本号提供给这样的批处理文件:

call foo.bat $(AssemblyVersion)

However I can't find any suitable variable or macro to use.

但是我找不到任何合适的变量或宏来使用。

Is there a way to achieve this that I've missed?

有没有办法实现我错过的这一目标?

采纳答案by Tuinstoelen

If you prefer scripting these methods might also work for you:

如果您更喜欢编写脚本,这些方法也可能适合您:

If you are using the post-build event, you can use the filever.exe tool to grab it out of the already built assembly:

如果你正在使用 post-build 事件,你可以使用 filever.exe 工具从已经构建的程序集中抓取它:

for /F "tokens=4" %%F in ('filever.exe /B /A /D bin\debug\myapp.exe') do (
  set VERSION=%%F
)
echo The version is %VERSION%

Get filever.exe from here: http://support.microsoft.com/kb/913111

从这里获取 filever.exe:http: //support.microsoft.com/kb/913111

If you are using the pre-build event, you can take it out of the AssemblyInfo.cs file as follows:

如果您使用的是预构建事件,您可以将其从 AssemblyInfo.cs 文件中取出,如下所示:

set ASMINFO=Properties\AssemblyInfo.cs
FINDSTR /C:"[assembly: AssemblyVersion(" %ASMINFO% | sed.exe "s/\[assembly: AssemblyVersion(\"/SET CURRENT_VERSION=/g;s/\")\]//g;s/\.\*//g" >SetCurrVer.cmd
CALL SetCurrVer.cmd
DEL SetCurrVer.cmd
echo Current version is %CURRENT_VERSION%

This uses the unix command line tool sed, which you can download from many places, such as here: http://unxutils.sourceforge.net/- iirc that one works ok.

这使用了 unix 命令行工具 sed,您可以从许多地方下载该工具,例如:http://unxutils.sourceforge.net/ - iirc 一个工作正常。

回答by Rohan West

I think the best thing you can do is look at MSBuild and MsBuild Extension Packyou should be able to edit you solution file so that a post build event occurs and writes to your test file.

我认为您能做的最好的事情是查看 MSBuild 和MsBuild 扩展包,您应该能够编辑您的解决方案文件,以便发生构建后事件并写入您的测试文件。

If this is too complicated then you could simply create a small program that inspects all assemblies in you output directory and execute it on post build, you could pass in the output directory using the variable name... for example in the post build event...

如果这太复杂,那么您可以简单地创建一个小程序来检查输出目录中的所有程序集并在构建后执行它,您可以使用变量名称传入输出目录......例如在构建后事件中。 ..

AssemblyInspector.exe "$(TargetPath)"

AssemblyInspector.exe "$(TargetPath)"

class Program
{
    static void Main(string[] args)
    {
        var assemblyFilename = args.FirstOrDefault();
        if(assemblyFilename != null && File.Exists(assemblyFilename))
        {
            try
            {
                var assembly = Assembly.ReflectionOnlyLoadFrom(assemblyFilename);
                var name = assembly.GetName();

                using(var file = File.AppendText("C:\AssemblyInfo.txt"))
                {
                    file.WriteLine("{0} - {1}", name.FullName, name.Version);
                }
            }
            catch (Exception ex)
            {
                throw;
            }
        }
    }
}

You could also pass in the text file location...

您还可以传入文本文件位置...

回答by Winston Smith

As a workaround I've written a managed console application which takes the target as a parameter, and returns the version number.

作为一种解决方法,我编写了一个托管控制台应用程序,它将目标作为参数,并返回版本号。

I'm still interested to hear a simpler solution - but I'm posting this in case anyone else finds it useful.

我仍然有兴趣听到一个更简单的解决方案 - 但我发布这个以防其他人发现它有用。

using System;
using System.IO;
using System.Diagnostics;
using System.Reflection;

namespace Version
{
    class GetVersion
    {
        static void Main(string[] args)
        {
            if (args.Length == 0 || args.Length > 1) { ShowUsage(); return; }

            string target = args[0];

            string path = Path.IsPathRooted(target) 
                                ? target 
                                : Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName) + Path.DirectorySeparatorChar + target;

            Console.Write( Assembly.LoadFile(path).GetName().Version.ToString(2) );
        }

        static void ShowUsage()
        {
            Console.WriteLine("Usage: version.exe <target>");
        }
    }
}

回答by Joshua Evensen

I've started adding a separate project that builds last and adding a post build event to that project that runs itself. Then I just perform my post build steps programmatically in there.

我已经开始添加一个单独的项目,该项目是最后构建的,并将一个构建后事件添加到该项目本身运行。然后我只是在那里以编程方式执行我的后期构建步骤。

It makes it a lot easier to do stuff like this. Then you can just inspect the assembly attributes of whatever assembly you want. So far it's working pretty awesome.

做这样的事情变得容易多了。然后你可以检查你想要的任何程序集的程序集属性。到目前为止,它的工作非常棒。

回答by Amen Ra

From that what I understand...

由此我明白...

You need a generator for post build events.

您需要一个生成后事件的生成器。

1. Step: Writing a Generator

1. 步骤:编写生成器

/*
* Author: Amen RA
* # Timestamp: 2013.01.24_02:08:03-UTC-ANKH
* Licence: General Public License
*/
using System;
using System.IO;

namespace AppCast
{
    class Program
    {
        public static void Main(string[] args)
        {
            // We are using two parameters.

            // The first one is the path of a build exe, i.e.: C:\pathto\nin\release\myapp.exe
            string exePath = args[0];

            // The second one is for a file we are going to generate with that information
            string castPath = args[1];

            // Now we use the methods below
            WriteAppCastFile(castPath, VersionInfo(exePath));
        }


        public static string VersionInfo(string filePath)
        {
            System.Diagnostics.FileVersionInfo myFileVersionInfo = System.Diagnostics.FileVersionInfo.GetVersionInfo(filePath);
            return myFileVersionInfo.FileVersion;
        }


        public static void WriteAppCastFile(string castPath, string exeVersion)
        {
            TextWriter tw = new StreamWriter(castPath);
            tw.WriteLine(@"<?xml version=""1.0"" encoding=""utf-8""?>");
            tw.WriteLine(@"<item>");
            tw.WriteLine(@"<title>MyApp - New version! Release " + exeVersion + " is available.</title>");
            tw.WriteLine(@"<version>" + exeVersion + "</version>");
            tw.WriteLine(@"<url>http://www.example.com/pathto/updates/MyApp.exe</url>");
            tw.WriteLine(@"<changelog>http://www.example.com/pathto/updates/MyApp_release_notes.html</changelog>");
            tw.WriteLine(@"</item>");
            tw.Close();
        }
    }
}

2. Step: Using it as a post build command in our IDE

2. 步骤:在我们的 IDE 中将其用作后期构建命令

After the application is running satisfyingly for you:

在应用程序为您满意地运行后:

In your development IDE, use the following command line for post build events.

在您的开发 IDE 中,将以下命令行用于后期构建事件。

C:\Projects\pathto\bin\Release\AppCast.exe "C:\Projects\pathto\bin\Release\MyApp.exe" "c:\pathto\www.example.com\root\pathto\updates\AppCast.xml"

回答by Brent Arias

If (1) you don't want to download or create a custom executable that retrieves the assembly version and (2) you don't mind editing the Visual Studio project file, then there is a simple solution that allows you to use a macro which looks like this:

如果 (1) 您不想下载或创建检索程序集版本的自定义可执行文件,并且 (2) 您不介意编辑 Visual Studio 项目文件,那么有一个简单的解决方案可以让您使用宏看起来像这样:

@(Targets->'%(Version)')

@(目标->'%(版本)')

@(VersionNumber)

To accomplish this, unload your project. If the project somewhere defines a <PostBuildEvent> property, cut it from the project and save it elsewhere temporarily (notepad?). Then at the very end of the project, just before the end-tag, place this:

为此,请卸载您的项目。如果项目某处定义了 <PostBuildEvent> 属性,请将其从项目中剪切并临时保存在其他地方(记事本?)。然后在项目的最后,就在结束标签之前,放置:

<Target Name="PostBuildMacros">
  <GetAssemblyIdentity AssemblyFiles="$(TargetPath)">
    <Output TaskParameter="Assemblies" ItemName="Targets" />
  </GetAssemblyIdentity>
  <ItemGroup>
    <VersionNumber Include="@(Targets->'%(Version)')"/>
  </ItemGroup>
</Target>
<PropertyGroup>
  <PostBuildEventDependsOn>
    $(PostBuildEventDependsOn);
    PostBuildMacros;
  </PostBuildEventDependsOn>    
  <PostBuildEvent>echo HELLO, THE ASSEMBLY VERSION IS: @(VersionNumber)</PostBuildEvent>
</PropertyGroup>

This snippet has an example <PostBuildEvent> already in it. No worries, you can reset it to your real post-build event after you have re-loaded the project.

此代码段中已包含一个示例 <PostBuildEvent>。不用担心,您可以在重新加载项目后将其重置为真正的构建后事件。

Now as promised, the assembly version is available to your post build event with this macro:

现在,正如承诺的那样,程序集版本可用于使用此宏的后期构建事件:

@(VersionNumber)

Done!

完毕!

回答by Nyerguds

I needed exactly this for automatically putting the number in the readme file in the output folder. In the end, as Winston Smith showed, a small external tool is a very good solution for that, and it has the advantage you can format it however you want.

我正是需要这个来自动将数字放入输出文件夹的自述文件中。最后,正如 Winston Smith 所展示的,一个小的外部工具是一个很好的解决方案,它的优点是你可以随意格式化它。

This app outputs the formatted version to the console. I used it in my post-build events to build the readme file by calling it with >>to redirect its output to the readme file.

此应用程序将格式化版本输出到控制台。我在构建后事件中使用它来构建自述文件,方法是调用它>>以将其输出重定向到自述文件。

public class GetVerNum
{
    static void Main(String[] args)
    {
        if (args.Length == 0)
            return;
        try
        {
            FileVersionInfo ver = FileVersionInfo.GetVersionInfo(args[0]);
            String version = "v" + ver.FileMajorPart.ToString() + "." + ver.FileMinorPart;
            if (ver.FileBuildPart > 0 || ver.FilePrivatePart > 0)
                version += "." + ver.FileBuildPart;
            if (ver.FilePrivatePart > 0)
                version += "." + ver.FilePrivatePart;
            Console.Write(version);
        }
        catch { }
    }
}

My post-build events:

我的构建后事件:

<nul set /p dummyset=My Application > "$(ProjectDir)\Readme\readme-header.txt"
"$(ProjectDir)\Readme\GetVersionNumber.exe" "$(TargetPath)" >>"$(ProjectDir)\Readme\readme-header.txt"
echo  by Nyerguds>>"$(ProjectDir)\Readme\readme-header.txt"
echo Build date: %date% %time% >> "$(ProjectDir)\Readme\readme-header.txt"
echo.>>"$(ProjectDir)\Readme\readme-header.txt"
copy /b "$(ProjectDir)\Readme\readme-header.txt" + "$(ProjectDir)\Readme\readme-body.txt" "$(TargetDir)$(ProjectName).txt"

I put all the readme generating related stuff in the \Readme\ folder of my project; the app containing the above code, and the "readme-body.txt" containing the actual readme stuff.

我把所有自述生成相关的东西放在我的项目的 \Readme\ 文件夹中;包含上述代码的应用程序,以及包含实际自述文件的“readme-body.txt”。

  • First line: create the "readme-header.txt" file in the \Readme\ folder of my project, and put the program name inside it. (The <nul set /p dummyset=is a trick I found here: Windows batch: echo without new line). You could also store this string in another text file and just copy that to "readme-header.txt" instead.
  • Second line: run the version number retrieving app with the freshly-generated exe file as parameter, and add its output to the header file.
  • Third line: add any other stuff (in this case, credits) to the header file. This also adds a line break to the end.
  • 第一行:在我的项目的\Readme\文件夹中创建“readme-header.txt”文件,并将程序名称放入其中。(这<nul set /p dummyset=是我在这里找到的一个技巧:Windows batch: echo without new line)。您也可以将此字符串存储在另一个文本文件中,然后将其复制到“readme-header.txt”。
  • 第二行:以新生成的exe文件为参数运行版本号检索应用,并将其输出添加到头文件中。
  • 第三行:向头文件中添加任何其他内容(在本例中为 credits)。这也会在末尾添加换行符。

These three together give you a "readme-header.txt" file with "My Application v1.2.3 by Nyerguds", followed by a line break, in it. Then I add the build date and another open line, and copy the header file and the readme body file together to one file in the final build folder. Note that I specifically use binary copy, otherwise it gives odd results. You do have to make sure the body file contains no UTF-8 byte order mark at the start, or you get weird bytes in your final file.

这三个一起为您提供了一个“readme-header.txt”文件,其中包含“My Application v1.2.3 by Nyerguds”,后跟一个换行符。然后我添加构建日期和另一个打开的行,并将头文件和自述正文文件一起复制到最终构建文件夹中的一个文件中。请注意,我专门使用二进制副本,否则会产生奇怪的结果。您必须确保正文文件在开始时不包含 UTF-8 字节顺序标记,否则最终文件中会出现奇怪的字节。

回答by Eric Boumendil

This answer is a minor modification of the answer of Brent Arias. His PostBuildMacro worked quite well for me until a version update of Nuget.exe.

这个答案是对布伦特阿里亚斯的答案的一个小修改。在 Nuget.exe 的版本更新之前,他的 PostBuildMacro 对我来说效果很好。

In the recent releases, Nuget trims non significant parts of the package version number in order to obtain a semantic version like "1.2.3". For example, the assembly version "1.2.3.0" is formatted by Nuget.exe "1.2.3". And "1.2.3.1" is formatted "1.2.3.1" as expected.

在最近的版本中,Nuget 修剪了包版本号的不重要部分,以获得像“1.2.3”这样的语义版本。例如,程序集版本“1.2.3.0”的格式为 Nuget.exe“1.2.3”。并且“1.2.3.1”按预期格式化为“1.2.3.1”。

As I need to infer the exact package filename generated by Nuget.exe, I use now this adaptated macro (tested in VS2015):

因为我需要推断 Nuget.exe 生成的确切包文件名,所以我现在使用这个改编的宏(在 VS2015 中测试):

<Target Name="PostBuildMacros">
  <GetAssemblyIdentity AssemblyFiles="$(TargetPath)">
    <Output TaskParameter="Assemblies" ItemName="Targets" />
  </GetAssemblyIdentity>
  <ItemGroup>
    <VersionNumber Include="$([System.Text.RegularExpressions.Regex]::Replace(&quot;%(Targets.Version)&quot;, &quot;^(.+?)(\.0+)$&quot;, &quot;&quot;))" />
  </ItemGroup>
</Target>
<PropertyGroup>
  <PostBuildEventDependsOn>
    $(PostBuildEventDependsOn);
    PostBuildMacros;
  </PostBuildEventDependsOn>    
  <PostBuildEvent>echo HELLO, THE ASSEMBLY VERSION IS: @(VersionNumber)</PostBuildEvent>
</PropertyGroup>

UPDATE 2017-05-24:I corrected the regex in this way: "1.2.0.0" will be translated to "1.2.0" and not "1.2" as previously coded.

更新 2017-05-24:我以这种方式更正了正则表达式:“1.2.0.0”将被转换为“1.2.0”而不是之前编码的“1.2”。



And to answer to a comment of Ehryk Apr, you can adapt the regex to keep only some part of the version number. As an example to keep "Major.Minor", replace:

并回答 Ehryk Apr 的评论,您可以调整正则表达式以仅保留部分版本号。以保留“Major.Minor”为例,替换:

<VersionNumber Include="$([System.Text.RegularExpressions.Regex]::Replace(&quot;%(Targets.Version)&quot;, &quot;^(.+?)(\.0+)$&quot;, &quot;&quot;))" />

By

经过

<VersionNumber Include="$([System.Text.RegularExpressions.Regex]::Replace(&quot;%(Targets.Version)&quot;, &quot;^([^\.]+)\.([^\.]+)(.*)$&quot;, &quot;.&quot;))" />

回答by DolceVita

If you have a library project you can try to use WMIC utility (available in windows). Here is an example. Good thing - you don't need to use any external tools.

如果您有一个库项目,您可以尝试使用 WMIC 实用程序(在 Windows 中可用)。这是一个例子。好事 - 您不需要使用任何外部工具。

SET pathFile=$(TargetPath.Replace("\", "\"))

FOR /F "delims== tokens=2" %%x IN ('WMIC DATAFILE WHERE "name='%pathFile%'" get  Version /format:Textvaluelist')  DO (SET dllVersion=%%x)
echo Found $(ProjectName) version %dllVersion%

回答by Momo

I looked for the same feature and i found the solution on MSDN. https://social.msdn.microsoft.com/Forums/vstudio/de-DE/e9485c92-98e7-4874-9310-720957fea677/assembly-version-in-post-build-event?forum=msbuild

我寻找了相同的功能,并在 MSDN 上找到了解决方案。 https://social.msdn.microsoft.com/Forums/vstudio/de-DE/e9485c92-98e7-4874-9310-720957fea677/assembly-version-in-post-build-event?forum=msbuild

$(ApplicationVersion) did the Job for me.

$(ApplicationVersion) 为我完成了这项工作。

Edit:

编辑:

Okay I just saw the Problem $(ApplicationVersion) is not from AssemblyInfo.cs, its the PublishVersion defined in the project Properties. It still does the job for me in a simple way. So maybe someone needs it too.

好的,我刚刚看到问题 $(ApplicationVersion) 不是来自 AssemblyInfo.cs,而是在项目属性中定义的 PublishVersion。它仍然以一种简单的方式为我完成工作。所以也许有人也需要它。

Another Solution:

另一个解决方案:

You can call a PowerShell script on PostBuild, here you can read the AssemblyVersion directly from your Assembly. I call the script with the TargetDir as Parameter

您可以在 PostBuild 上调用 PowerShell 脚本,在这里您可以直接从您的程序集中读取 AssemblyVersion。我用 TargetDir 作为参数调用脚本

PostBuild Command:

构建后命令:

PowerShell -ExecutionPolicy Unrestricted $(ProjectDir)\somescript.ps1 -TargetDir $(TargetDir)

PowerShell Script:

PowerShell 脚本:

param(
    [string]$TargetDir
)

$Version = (Get-Command ${TargetDir}Example.exe).FileVersionInfo.FileVersion

This way you will get the Version from the AssemblyInfo.cs

通过这种方式,您将从 AssemblyInfo.cs 中获取版本