visual-studio 如何在 VS 安装项目输出文件名中包含版本号

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3359974/
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-10-22 12:16:23  来源:igfitidea点击:

How to include version number in VS Setup Project output filename

visual-studiovisual-studio-2008setup-project

提问by chardy

Is there a way to include the version number as part of the output.msi filename in a VS2008 Setup Project?

有没有办法在 VS2008 安装项目中包含版本号作为 output.msi 文件名的一部分?

I'd like for example an output file called: "myinstaller-1.0.13.msi" where the version part is automatically set based on the version number I have put in the deployment project properties.

例如,我想要一个名为“myinstaller-1.0.13.msi”的输出文件,其中版本部分是根据我在部署项目属性中放入的版本号自动设置的。

采纳答案by JPReddy

Not sure whether you still require this or not but wanted answer this as we did similar kind of operation in the postbuild event. As far as the research I did this is not possible to set the file name as you want internally through setup process.

不确定您是否仍然需要这个,但想回答这个问题,因为我们在 postbuild 事件中做了类似的操作。就我所做的研究而言,不可能通过设置过程在内部设置您想要的文件名。

You can do this in other way by naming the output file through an external application in post build event.

您可以通过在构建后事件中通过外部应用程序命名输出文件来以其他方式执行此操作。

Here is what you can do:

您可以执行以下操作:

In the post build event ->

在后期构建事件中 ->

[MsiRenamerAppPath]\MsiRenamer.exe "$(BuildOutputPath)"

[MsiRenamerAppPath]\MsiRenamer.exe "$(BuildOutputPath)"

Create an application which will rename the msi file with the version number from the deployment project. Following is the code used for the application. This should fulfill your requirement I guess.

创建一个应用程序,该应用程序将使用部署项目中的版本号重命名 msi 文件。以下是用于该应用程序的代码。我想这应该满足您的要求。

Getting msi properties code is used from alteridem article

alteridem文章中使用获取msi属性代码

class MsiRenamer
  {
    static void Main(string[] args)
    {
      string inputFile;
      string productName = "[ProductName]";

      if (args.Length == 0)
      {
        Console.WriteLine("Enter MSI file:");
        inputFile = Console.ReadLine();
      }
      else
      {
        inputFile = args[0];
      }

      try
      {
        string version;

        if (inputFile.EndsWith(".msi", StringComparison.OrdinalIgnoreCase))
        {
          // Read the MSI property
          version = GetMsiProperty(inputFile, "ProductVersion");
          productName = GetMsiProperty(inputFile, "ProductName");
        }
        else
        {
          return;
        }
        // Edit: MarkLakata: .msi extension is added back to filename
        File.Copy(inputFile, string.Format("{0} {1}.msi", productName, version));
        File.Delete(inputFile);
      }
      catch (Exception ex)
      {
        Console.WriteLine(ex.Message);
      }
    }

    static string GetMsiProperty(string msiFile, string property)
    {
      string retVal = string.Empty;

      // Create an Installer instance  
      Type classType = Type.GetTypeFromProgID("WindowsInstaller.Installer");
      Object installerObj = Activator.CreateInstance(classType);
      Installer installer = installerObj as Installer;

      // Open the msi file for reading  
      // 0 - Read, 1 - Read/Write  
      Database database = installer.OpenDatabase(msiFile, 0);

      // Fetch the requested property  
      string sql = String.Format(
          "SELECT Value FROM Property WHERE Property='{0}'", property);
      View view = database.OpenView(sql);
      view.Execute(null);

      // Read in the fetched record  
      Record record = view.Fetch();
      if (record != null)
      {
        retVal = record.get_StringData(1);
        System.Runtime.InteropServices.Marshal.FinalReleaseComObject(record);
      }
      view.Close();
      System.Runtime.InteropServices.Marshal.FinalReleaseComObject(view);
      System.Runtime.InteropServices.Marshal.FinalReleaseComObject(database);

      return retVal;
    }
  }

回答by Jim Grimmett

I didn't want to use the .exe method above and had a little time spare so I started diggind around. I'm using VS 2008 on Windows 7 64 bit. When I have a Setup project, lets call it MySetup all the details of the project can be found in the file $(ProjectDir)MySetup.vdproj.

我不想使用上面的 .exe 方法并且有一点空闲时间,所以我开始四处寻找。我在 Windows 7 64 位上使用 VS 2008。当我有一个安装项目时,我们称之为 MySetup。项目的所有细节都可以在 $(ProjectDir)MySetup.vdproj 文件中找到。

The product version will be found on a single line in that file in the form

产品版本将在该文件的单行中找到,格式为

ProductVersion="8:1.0.0"

Now, there IS a post-build event on a setup project. If you select a setup project and hit F4 you get a completely different set of properties to when you right-click and select properties. After hitting F4 you'll see that one of the is PostBuildEvent. Again assuming that the setup project is called MySetup the following will set the name of the .msi to include the date and the version

现在,安装项目有一个构建后事件。如果您选择一个安装项目并按 F4,您将获得与右键单击并选择属性时完全不同的一组属性。按 F4 后,您将看到其中一个是 PostBuildEvent。再次假设安装项目名为 MySetup,以下将设置 .msi 的名称以包括日期和版本

set datevar=%DATE:~6,4%%DATE:~3,2%%DATE:~0,2%
findstr /v PostBuildEvent $(ProjectDir)MySetup.vdproj | findstr ProductVersion >$(ProjectDir)version.txt
set /p var=<$(ProjectDir)version.txt
set var=%var:"=%
set var=%var: =%
set var=%var:.=_%
for /f "tokens=1,2 delims=:" %%i in ("%var%") do @echo %%j >$(ProjectDir)version.txt
set /p realvar=<$(ProjectDir)version.txt
rename "$(ProjectDir)$(Configuration)\MySetup.msi" "MySetup-%datevar%-%realvar%.msi"

I'll take you through the above.

我会带你完成上面的。

datevar is the current date in the form YYYYMMDD.

datevar 是格式为 YYYYMMDD 的当前日期。

The findstr line goes through MySetup.vdproj, removes any line with PostBuildEvent in, then returns the single line left with productVersion in, and outputs it to a file. We then remove the quotes, spaces, turn dots into underscores.

findstr 行通过 MySetup.vdproj,删除任何带有 PostBuildEvent 的行,然后返回带有 productVersion 的单行,并将其输出到文件中。然后我们删除引号,空格,将点变成下划线。

The for line splits the remaining string on colon, and takes the second part, and outputs it to a file again.

for 行在冒号处拆分剩余的字符串,并取第二部分,并将其再次输出到文件中。

We then set realvar to the value left in the file, and rename MySetup.msi to include the date and version.

然后我们将 realvar 设置为文件中剩余的值,并重命名 MySetup.msi 以包含日期和版本。

So, given the ProductVersion above, if it was 27th March 2012 the file would be renamed to

因此,鉴于上面的 ProductVersion,如果是 2012 年 3 月 27 日,该文件将重命名为

MySetup-20120327-1_0_0.msi

Clearly using this method you could grab ANY of the variables in the vdproj file and include them in your output file name and we don't have to build any extra .exe programs to do it.

显然,使用这种方法您可以获取 vdproj 文件中的任何变量并将它们包含在您的输出文件名中,我们不必构建任何额外的 .exe 程序来执行此操作。

HTH

HTH

回答by antak

Same concept as Jim Grimmett's answer, but with less dependencies:

与 Jim Grimmett 的答案相同的概念,但依赖较少:

FOR /F "tokens=2 delims== " %%V IN ('FINDSTR /B /R /C:" *\"ProductVersion\"" "$(ProjectDir)MySetupProjectName.vdproj"') DO FOR %%I IN ("$(BuiltOuputPath)") DO REN "$(BuiltOuputPath)" "%%~nI-%%~nxV%%~xI"

Some points of note:

一些注意事项:

MySetupProjectName.vdprojshould be changed to the name of your project file. Forgetting to change this results in a build error: 'PostBuildEvent' failed with error code '1'and the Outputwindow shows which file FINDSTRcould not open.

MySetupProjectName.vdproj应该更改为您的项目文件的名称。忘记更改这会导致构建错误:'PostBuildEvent' failed with error code '1'并且Output窗口显示FINDSTR无法打开哪个文件。

Step by step description:

分步说明:

FINDSTR /B /R /C:" *\"ProductVersion\"" $(ProjectDir)MySetupProjectName.vdproj

FINDSTR /B /R /C:" *\"ProductVersion\"" $(ProjectDir)MySetupProjectName.vdproj

  • This finds the "ProductVersion" = "8:x.y.z.etc"line from the project file.
  • 这会"ProductVersion" = "8:x.y.z.etc"从项目文件中找到该行。

FOR /F "tokens=2 delims== " %%V IN (...) DO ... %%~nxV ...

FOR /F "tokens=2 delims== " %%V IN (...) DO ... %%~nxV ...

  • This is used to parse out the x.y.z.etcpart from the above result.
  • 这用于x.y.z.etc从上述结果中解析出部分。

$(BuiltOuputPath)

$(BuiltOuputPath)

  • This is the original output path, as per what it says in Post-build Event Command Line's "Macros".
  • 这是原始输出路径,根据构建后事件命令行的“宏”中的说明。

FOR %%I IN (...) DO ... %%~nI-%%~nxV%%~xI

FOR %%I IN (...) DO ... %%~nI-%%~nxV%%~xI

  • This is used to convert the string foo.msito foo-x.y.z.etc.msi.
  • 这用于将字符串转换foo.msifoo-x.y.z.etc.msi.

REN "$(BuiltOuputPath)" ...

REN "$(BuiltOuputPath)" ...

  • This just renames the output path to the new name.
  • 这只是将输出路径重命名为新名称。

FOR ... DO FOR .. DO REN ...

FOR ... DO FOR .. DO REN ...

  • It's written on one line like this so that an error along way cleanly breaks the build.
  • 它写在这样的一行上,以便沿途的错误干净地破坏构建。

回答by prairiehat

If you use a WIX project (as opposed to a VS Setup & Deployment project) then this articleexplains exactly how to achieve what you are after.

如果您使用 WIX 项目(而不是 VS 安装和部署项目),那么本文将准确解释如何实现您的目标。

回答by Evvo

I did it with 2 lines in powershell.

我在 powershell 中用 2 行代码做到了。

$versionText=(Get-Item MyProgram.exe).VersionInfo.FileVersion
(Get-Content MySetup.vdproj.template).replace('${VERSION}', $($versionText)) | Set-Content MySetup.vdproj

Rename your existing .vdproj to be MySetup.vdproj.template and insert "${VERSION}" wherever you want to insert the version of your primary exe file.

将现有的 .vdproj 重命名为 MySetup.vdproj.template 并在要插入主 exe 文件版本的任何位置插入“${VERSION}”。

VS will then detect the change in the vdproj file and ask you if you want to reload it.

然后 VS 会检测到 vdproj 文件中的更改并询问您是否要重新加载它。

回答by Muhammad Mubashir

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using WindowsInstaller;


// cscript //nologo "$(ProjectDir)WiRunSql.vbs" "$(BuiltOuputPath)" "UPDATE `Property` SET `Property`.`Value`='4.0.0.1' WHERE `Property`='ProductVersion'"
// "SELECT `Property`.`ProductVersion` FROM `Property` WHERE `Property`.`Property` = 'ProductVersion'"

/* 
 * That's a .NET wrapper generated by tlbimp.exe, wrapping the ActiveX component c:\windows\system32\msi.dll.  
 * You can let the IDE make one for you with Project + Add Reference, COM tab, 
 * select "Microsoft Windows Installer Object Library". 
 */
namespace PostBuildEventModifyMSI
{
    /* Post build event fro Rename MSI file.
     * $(SolutionDir)PostBuildEventModifyMSI\bin\Debug\PostBuildEventModifyMSI.exe "$(SolutionDir)TestWebApplicationSetup\Debug\TestWebApplicationSetup.msi"
     */

    [System.Runtime.InteropServices.ComImport(), System.Runtime.InteropServices.Guid("000C1090-0000-0000-C000-000000000046")]
    class Installer { }
    class Program
    {
        static void Main(string[] args)
        {
            #region New code.

            string msiFilePath = string.Empty;
            if (args.Length == 0)
            {
                Console.WriteLine("Enter MSI file complete path:");
                msiFilePath = Console.ReadLine();
            }
            else
            {
                Console.WriteLine("Argument Received args[0]: " + args[0]);
                msiFilePath = args[0];
            }

            StringBuilder sb = new StringBuilder();
            string[] words = msiFilePath.Split('\');
            foreach (string word in words)
            {
                sb.Append(word + '\');

                if (word.Contains("Debug"))
                {
                    break;
                }
                else
                {

                }
            }

            // Open a view on the Property table for the Label property 
            //UPDATE Property set Value = '2.06.36' where Property = 'ProductVersion'
            Program p = new Program();
            string version = p.GetMsiVersionProperty(msiFilePath, "ProductVersion");
            string productName = p.GetMsiVersionProperty(msiFilePath, "ProductName");

            string newMSIpath = sb.ToString() + string.Format("{0}_{1}.msi", productName, version);
            Console.WriteLine("Original MSI File Path: " + msiFilePath);
            Console.WriteLine("New MSI File Path: " + newMSIpath);


            System.IO.File.Move(msiFilePath, newMSIpath);

            #endregion




            //Console.Read();
        }

        private string GetMsiVersionProperty(string msiFilePath, string property)
        {
            string retVal = string.Empty;

            // Create an Installer instance  
            WindowsInstaller.Installer installer = (WindowsInstaller.Installer) new Installer();

            // Open the msi file for reading  
            // 0 - Read, 1 - Read/Write  
            Database db = installer.OpenDatabase(msiFilePath, WindowsInstaller.MsiOpenDatabaseMode.msiOpenDatabaseModeReadOnly); //// Open the MSI database in the input file 

            // Fetch the requested property  
            string sql = String.Format(
                "SELECT Value FROM Property WHERE Property='{0}'", property);
            View view = db.OpenView(sql);
            //View vw = db.OpenView(@"SELECT `Value` FROM `Property` WHERE `Property` = 'ProductVersion'");
            view.Execute(null);

            // Read in the fetched record  
            Record record = view.Fetch();
            if (record != null)
            {
                retVal = record.get_StringData(1);
                System.Runtime.InteropServices.Marshal.FinalReleaseComObject(record);
            }
            view.Close();

            System.Runtime.InteropServices.Marshal.FinalReleaseComObject(view);
            System.Runtime.InteropServices.Marshal.FinalReleaseComObject(db);

            return retVal;
        }

    }
}