Visual Studio 如何在VS安装项目输出文件名中包含版本号

9cbw7uwe  于 2023-01-09  发布在  其他
关注(0)|答案(7)|浏览(225)

是否有办法将版本号作为output.msi文件名的一部分包含在VS 2008安装项目中?
例如,我想要一个名为:“myinstaller-1.0.13.msi”,其中版本部分是根据我在部署项目属性中放置的版本号自动设置的。

g6ll5ycj

g6ll5ycj1#

如果您使用WIX项目(与VS Setup & Deployment项目相反),那么this article将准确地解释如何实现您所追求的目标。

rkkpypqq

rkkpypqq2#

我用powershell中的2行代码完成了它。

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

将现有的.vdproj重命名为MySetup.vdproj.template,并在要插入主exe文件版本的位置插入“${VERSION}”。
VS将检测vdproj文件中的更改,并询问您是否要重新加载它。

g0czyy6m

g0czyy6m3#

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

    }
}
5fjcxozz

5fjcxozz4#

我对@JPreddy提交做了一些修改,使其具有用于重命名的输入和输出文件夹以及一些日志记录。
使用.net framework 4.8创建和新建控制台项目下载here
下面是如何安装和使用。转到您的安装程序项目下,然后单击“PostBuildEvents”。在参数中,您将需要调用.exe,然后添加变量/宏,如下所示:
EXE输入文件夹输出文件夹

C:\temp\MsiRenamer.exe $(BuiltOuputPath) $(ProjectDir)$(Configuration)\

下面是应用程序的app.config代码

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <configSections>
        <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net" />
    </configSections>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" />
    </startup>
    <log4net>
        <appender name="MyConsole" type="log4net.Appender.ConsoleAppender">
            <file value="${ProgramData}\MsiRenamer\Log\MsiRenamer_" />
            <datepattern value="yyyyMMdd'_${USERNAME}.log'" />
            <threshold value="ALL" />
            <appendToFile value="true" />
            <RollingStyle value="size,date" />
            <maximumFileSize value="1MB" />
            <maxSizeRollBackups value="10" />
            <layout type="log4net.Layout.PatternLayout">
                <!-- Pattern to output the caller's file name and line number -->
                <conversionPattern value="%date [%thread] %-5level %logger [%identity] | %message%newline" />
            </layout>
        </appender>
        <appender name="MyColoredConsoleAppender" type="log4net.Appender.ColoredConsoleAppender">
            <mapping>
                <level value="ERROR" />
                <foreColor value="Red, highintensity" />
            </mapping>
            <mapping>
                <level value="WARN" />
                <foreColor value="Yellow, highintensity" />
            </mapping>
            <mapping>
                <level value="ALL" />
                <foreColor value="Green, highintensity" />
            </mapping>
            <layout type="log4net.Layout.PatternLayout">
                <conversionPattern value="*%-10level %-30date %message [%logger] [%thread] %newline" />
            </layout>
        </appender>
        <appender name="MyFileAppender" type="log4net.Appender.rollingFileAppender">
            <file value="${ProgramData}\MsiRenamer\Log\MsiRenamer_" />
            <datepattern value="yyyyMMdd'_${USERNAME}.log'" />
            <threshold value="ALL" />
            <appendToFile value="true" />
            <maximumFileSize value="1MB" />
            <MaxSizeRollBackups value="10" />
            <RollingStyle value="size,date" />
            <staticLogFileName value="false" />
            <preserveLogFileNameExtension value="true" />
            <layout type="log4net.Layout.PatternLayout">
                <conversionPattern value="%date [%thread] %-5level %logger [%identity] | %message%newline" />
            </layout>
        </appender>
        <appender name="MyMemoryAppender" type="log4net.Appender.MemoryAppender">
        </appender>
        <appender name="RichTextBoxAppender" type="Sample.RichTextBoxAppender, MyFFLBookAPIImport">
            <formName value="FrmSmple" />
            <textBoxName value="RtbOutput" />
            <layout type="log4net.Layout.PatternLayout">
                <conversionPattern value="%date %-5level - %message%newline" />
            </layout>
        </appender>
        <root>
            <level value="ALL" />
            <appender-ref ref="MyFileAppender" />
            <appender-ref ref="MyConsole" />
            <appender-ref ref="MyColoredConsoleAppender" />
            <appender-ref ref="MyMemoryAppender" />
            <appender-ref ref="RichTextBoxAppender" />
        </root>
    </log4net>
</configuration>

下面是该应用程序的C#代码:

using log4net;
using System;
using System.IO;
using WindowsInstaller;

namespace MsiRenamer
{

    internal class MsiRenamer
    {
        private static readonly ILog _log = LogManager.GetLogger(typeof(MsiRenamer));

        static void Main(string[] args)

        {
            log4net.Config.XmlConfigurator.Configure();
            string inputFile;
            string outputFolder;
            string outputFileName;
            string productName = "[ProductName]";

            if (args.Length == 0)
            {
                Console.WriteLine("Enter MSI Input PathFileName:");
                inputFile = Console.ReadLine();
                _log.Info("InputFile: " + inputFile);
                Console.WriteLine("Enter MSI Output Folder:");
                outputFolder = Console.ReadLine();
                _log.Info("Output Folder: " + outputFolder);

            }
            else
            {
                inputFile = args[0];
                _log.Info("InputFile: " + inputFile);
                outputFolder = args[1];
                _log.Info("Output Folder: " + outputFolder);
            }

            try
            {
                string version;

                if (inputFile.EndsWith(".msi", StringComparison.OrdinalIgnoreCase))
                {
                    // Read the MSI property
                    version = GetMsiProperty(inputFile, "ProductVersion");
                    _log.Info("Version: " + version);
                    productName = GetMsiProperty(inputFile, "ProductName");
                    _log.Info("ProductName: " + productName);
                }
                else
                {
                    return;
                }
      
                outputFileName = outputFolder + string.Format("{0}{1}.msi", productName, version);
                if (File.Exists(outputFileName))
                {
                    File.Delete(outputFileName);
                    _log.Info("Delete existing file :" + outputFileName);
                }

                _log.Info("OutputFileName: " + outputFileName);
                File.Copy(inputFile, outputFileName);
                _log.Info("Copied file from : " + inputFile + " to " + outputFileName);
                //File.Copy(inputFile, string.Format("{0} {1}.msi", productName, version));
                File.Delete(inputFile);
                _log.Info("Deleting original file :" + inputFile);
            }
            catch (Exception ex)
            {
                _log.Error(ex.Message);
                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;
        }
    }
}
ftf50wuq

ftf50wuq5#

我不想使用上面的.exe方法,并且有一点空闲时间,所以我开始挖掘。我在Windows 7 64位上使用VS 2008。当我有一个安装项目时,让我们称之为MySetup,项目的所有细节都可以在文件$(ProjectDir)MySetup. vdproj中找到。
产品版本将显示在该文件的一行中,格式为

ProductVersion="8:1.0.0"

现在,安装项目上有一个生成后事件。如果选择安装项目并按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以包含日期和版本。
因此,根据上述产品版本,如果日期为2012年3月27日,则该文件将重命名为

MySetup-20120327-1_0_0.msi

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

fnvucqvd

fnvucqvd6#

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

  • 这是原始的输出路径,正如它在构建后事件命令行的“宏”中所说的那样。

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

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

REN "$(BuiltOuputPath)" ...

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

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

  • 它像这样写在一行上,这样一个错误沿着彻底破坏构建。
xghobddn

xghobddn7#

不确定您是否仍然需要此问题,但希望回答此问题,因为我们在生成后事件中执行了类似的操作。就我所做的研究而言,这是不可能通过安装过程在内部设置您想要的文件名的。
您也可以通过其他方式完成此操作,方法是在生成后事件中通过外部应用程序命名输出文件。
以下是您可以执行的操作:
在生成后事件中-〉
[MsiRenamer应用程序路径]\MsiRenamer. exe "$(生成输出路径)"
创建一个应用程序,该应用程序将使用部署项目中的版本号重命名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;
    }
  }

相关问题