如何在VS Setup Project输出文件名中包含版本号

时间:2010-07-29 06:31:21

标签: visual-studio visual-studio-2008 setup-project

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

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

6 个答案:

答案 0 :(得分:17)

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

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

ProductVersion="8:1.0.0"

现在,在安装项目上有一个post-build事件。如果选择一个安装项目并点击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"

我会带你了解上述内容。

datevar是YYYYMMDD形式的当前日期。

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

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

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

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

MySetup-20120327-1_0_0.msi

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

HTH

答案 1 :(得分:13)

与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"

有些注意事项:

MySetupProjectName.vdproj应更改为项目文件的名称。忘记更改此操作会导致生成错误:'PostBuildEvent' failed with error code '1'并且Output窗口会显示无法打开的文件FINDSTR

分步说明:

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

  • 从项目文件中找到"ProductVersion" = "8:x.y.z.etc"行。

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

  • 这用于解析上述结果中的x.y.z.etc部分。

$(BuiltOuputPath)

  • 这是原始输出路径,根据其在构建后事件命令行&#34;宏&#34;中所述的内容。

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

  • 这用于将字符串foo.msi转换为foo-x.y.z.etc.msi

REN "$(BuiltOuputPath)" ...

  • 这只是将输出路径重命名为新名称。

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

  • 它写在这样的一行上,以便一路上的错误干净地打破了构建。

答案 2 :(得分:12)

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

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

以下是您可以做的事情:

在帖子构建活动中 - &gt;

  

[MsiRenamerAppPath] \ MsiRenamer.exe“$(BuildOutputPath)”

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

alteridem article

使用获取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;
    }
  }

答案 3 :(得分:2)

如果您使用WIX项目(而不是VS Setup&amp; Deployment项目),那么this article将准确地解释如何实现您的目标。

答案 4 :(得分:1)

我在Powershell中用2行做到了。

g

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

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

答案 5 :(得分:0)

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;
        }

    }
}