<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.9.3">Jekyll</generator><link href="https://danprice.dev/feed.xml" rel="self" type="application/atom+xml" /><link href="https://danprice.dev/" rel="alternate" type="text/html" /><updated>2023-06-29T10:56:56+00:00</updated><id>https://danprice.dev/feed.xml</id><title type="html">Dan Price</title><subtitle>.Net developer, IT guy, musician, retro gamer, digital artist.</subtitle><entry><title type="html">Automating version numbering in .Net Standard/Core projects</title><link href="https://danprice.dev/code/2022/04/19/version-numbering-2.html" rel="alternate" type="text/html" title="Automating version numbering in .Net Standard/Core projects" /><published>2022-04-19T00:00:00+00:00</published><updated>2022-04-19T00:00:00+00:00</updated><id>https://danprice.dev/code/2022/04/19/version-numbering-2</id><content type="html" xml:base="https://danprice.dev/code/2022/04/19/version-numbering-2.html">&lt;p&gt;Note: This is the second in an irregular series on automating version numbering. For part 1, &lt;a href=&quot;/code/2020/06/12/automating-version-numbering.html&quot;&gt;click here&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;a-bit-of-background&quot;&gt;A bit of background.&lt;/h2&gt;

&lt;p&gt;WinForms projects (and Windows programs in general) use a 4 part version number in the format &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[major].[minor].[build].[revision]&lt;/code&gt;. In previous versions of the .Net Framework we were able to tell MSBuild to automatically increment parts of these by setting either the revision or build part of the version number to an asterisk (*), either directly in &lt;em&gt;AssemblyInfo.cs&lt;/em&gt; or in the Visual Studio UI, which then updates the file itself.&lt;/p&gt;

&lt;p&gt;However, .Net 5 (and its predecessors .Net Core and .Net Standard) depart from this behaviour by automatically generating &lt;em&gt;AssemblyInfo.cs&lt;/em&gt;, leaving us devs with a conundrum - we don’t want the backward step of having to increment our version numbers by hand again, how can we get the computer to do it for us?&lt;/p&gt;

&lt;h2 id=&quot;the-net-5-approach&quot;&gt;The .Net 5 approach&lt;/h2&gt;

&lt;p&gt;In .Net 5, everything we need to automate is now set up from the .csproj file - and even better, MSBuild lets us use variables and conditional code to generate our output. To begin with, let’s look at this excellent example from multiple-time MVP Sacha Barber &lt;a href=&quot;https://sachabarbs.wordpress.com/2020/02/23/net-core-standard-auto-incrementing-versioning/&quot;&gt;(link)&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;VersionSuffix&amp;gt;&lt;/span&gt;1.0.0.$([System.DateTime]::UtcNow.ToString(mmff))&lt;span class=&quot;nt&quot;&gt;&amp;lt;/VersionSuffix&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;AssemblyVersion&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; '$(VersionSuffix)' == '' &quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;0.0.0.1&lt;span class=&quot;nt&quot;&gt;&amp;lt;/AssemblyVersion&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;AssemblyVersion&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; '$(VersionSuffix)' != '' &quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;$(VersionSuffix)&lt;span class=&quot;nt&quot;&gt;&amp;lt;/AssemblyVersion&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Version&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; '$(VersionSuffix)' == '' &quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;0.0.1.0&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Version&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Version&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; '$(VersionSuffix)' != '' &quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;$(VersionSuffix)&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Version&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I’ve taken the liberty of stripping out most of the code from this to show just the salient points as Sacha’s excellent blog post included the whole .csproj file. As you can see, the file lets us define our own variables - &lt;em&gt;VersionSuffix&lt;/em&gt; is not a regular C# project file element. MSBuild also lets us use static methods from a lot of the .Net standard’s built-in types. At the moment all we’re interested in is System.DateTime, but for a complete list &lt;a href=&quot;https://docs.microsoft.com/en-us/visualstudio/msbuild/property-functions?view=vs-2022&quot;&gt;see here&lt;/a&gt;. By defining multiple &lt;em&gt;AssemblyVersion&lt;/em&gt; (and &lt;em&gt;Version&lt;/em&gt;) elements, we can use the &lt;em&gt;Condition&lt;/em&gt; attribute to make sure &lt;em&gt;VersionSuffix&lt;/em&gt; has a value - if it does, we use that.&lt;/p&gt;

&lt;h2 id=&quot;taking-it-further&quot;&gt;Taking it further&lt;/h2&gt;

&lt;p&gt;This is great, but I wanted to try and replicate the exact numbering we get from earlier versions of MSBuild. In those, setting the build part of the version string to “*” would set that part to the number of days since the 1st of January 2000, and the revision part to the number of seconds since midnight divided by 2. Why Microsoft chose that specifically is beyond me, but since I was trying to get as close as possible to the old behaviour I wanted to implement it. This requires a little more work:&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Millennium&amp;gt;&lt;/span&gt;$([System.DateTime]::Parse(`2000,1,1`))&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Millennium&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;VersionBuildPart&amp;gt;&lt;/span&gt;$([System.DateTime]::UtcNow.Subtract($(Millennium)).Days)&lt;span class=&quot;nt&quot;&gt;&amp;lt;/VersionBuildPart&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;VersionRevisionPart&amp;gt;&lt;/span&gt;$([System.Convert]::ToUInt16($([MSBuild]::Divide($([System.DateTime]::UtcNow.TimeOfDay.TotalSeconds),2))))&lt;span class=&quot;nt&quot;&gt;&amp;lt;/VersionRevisionPart&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;AssemblyVersion&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; '$(VersionBuildPart)' == '' OR '$(VersionRevisionPart)' == ''&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;1.0.0.0&lt;span class=&quot;nt&quot;&gt;&amp;lt;/AssemblyVersion&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;AssemblyVersion&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; '$(VersionBuildPart)' != '' AND '$(VersionRevisionPart)' != ''&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;1.0.$(VersionBuildPart).$(VersionRevisionPart)&lt;span class=&quot;nt&quot;&gt;&amp;lt;/AssemblyVersion&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;How this works should be pretty obvious, but for the sake of completeness:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;We instantiate a new DateTime called &lt;em&gt;Millennium&lt;/em&gt; to 1/1/2000.&lt;/li&gt;
  &lt;li&gt;We subtract that from the static DateTime UtcNow and store the resulting TimeSpan’s Days property in a variable called &lt;em&gt;VersionBuildPart&lt;/em&gt;.&lt;/li&gt;
  &lt;li&gt;We divide the number of seconds from UtcNow by 2, convert it to an integer using an MSBuild built-in method and store that as &lt;em&gt;VersionRevisionPart&lt;/em&gt;.&lt;/li&gt;
  &lt;li&gt;If either part is blank, we set the &lt;em&gt;AssemblyVersion&lt;/em&gt; to a set value of 1.0.0.0. (Not likely, as it’s much more likely that the build process will fail if it can’t process the dates properly.)&lt;/li&gt;
  &lt;li&gt;Otherwise, we concatenate our set major and minor version numbers (1.0 in this case) with our calculated build and revision numbers.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;determinism&quot;&gt;Determinism&lt;/h2&gt;

&lt;p&gt;There’s one last hoop to jump through when automating this stuff: since the code is being modified at build-time, by necessity the resulting binary is going to differ with every build because of this, we need to tell MSBuild that we don’t want the build to be deterministic. Placing this in the same PropertyGroup as the above version numbering code is sufficient:&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Deterministic&amp;gt;&lt;/span&gt;False&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Deterministic&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now, hit build and watch as your version number increments itself just like it does in .Net Framework.&lt;/p&gt;</content><author><name></name></author><category term="Code" /><summary type="html">Note: This is the second in an irregular series on automating version numbering. For part 1, click here</summary></entry><entry><title type="html">Automating version numbering in Visual Studio Deployment Projects</title><link href="https://danprice.dev/code/2020/06/12/automating-version-numbering.html" rel="alternate" type="text/html" title="Automating version numbering in Visual Studio Deployment Projects" /><published>2020-06-12T00:00:00+00:00</published><updated>2020-06-12T00:00:00+00:00</updated><id>https://danprice.dev/code/2020/06/12/automating-version-numbering</id><content type="html" xml:base="https://danprice.dev/code/2020/06/12/automating-version-numbering.html">&lt;p&gt;For many years, Visual Studio has had the option to automatically increment a project’s version number when building that project. However, deployment projects use their own version numbers and ignore the version number of the project they’re deploying. Here’s how you can automate the deployment project’s (and therefore the compiled MSI’s) version number to match the product you’re deploying.&lt;/p&gt;

&lt;h2 id=&quot;a-note-on-solution-and-msi-version-numbers&quot;&gt;A note on solution and MSI version numbers&lt;/h2&gt;

&lt;p&gt;WinForms projects (and Windows programs in general) use a 4 part version number in the format &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[major].[minor].[build].[revision]&lt;/code&gt;. Windows Installers (MSIs) use a 3-part version number, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[major].[minor].[build]&lt;/code&gt;. Because of this, when we get Visual Studio to increment the project’s version number we’re going to have to lose some information to get it into the MSI.&lt;/p&gt;

&lt;h2 id=&quot;automatically-incrementing-the-assembly-project-version&quot;&gt;Automatically incrementing the assembly (project) version&lt;/h2&gt;

&lt;p&gt;We’re going to make Visual Studio (or more accurately, MSBuild) handle the &lt;em&gt;build&lt;/em&gt; and &lt;em&gt;revision&lt;/em&gt; parts of the version number. As specified in &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.reflection.assemblyversionattribute#remarks&quot;&gt;the docs&lt;/a&gt;, accepting the defaults for these will set the build number to a value that increases daily (technically, the number of days since 1 January 2000) and the release number to the number of seconds since local midnight, divided by 2. Note that since the revision number is discarded by this script when we apply it to our MSI, building 2 MSIs on the same day will result in them having the same version number even if the assembly revisions are different.&lt;/p&gt;

&lt;p&gt;First things first, we need to make VS handle the assembly’s version number. Bizzarely, the Assembly Information dialog you would usually use to change this will not accept the perfectly valid values we’re going to set it to so we’ll have to do it manually.&lt;/p&gt;

&lt;p&gt;In Solution Explorer, expand your application’s &lt;em&gt;Properties&lt;/em&gt; section and open the &lt;em&gt;AssemblyInfo.cs&lt;/em&gt; (or &lt;em&gt;.vb&lt;/em&gt;, if that’s your preference) file. Right at the bottom, you should see annotations for  &lt;em&gt;AssemblyVersion&lt;/em&gt; and &lt;em&gt;AssemblyFileVersion&lt;/em&gt; along with a comment on how to set them dynamically. Following Microsoft’s guidance, we’re going to set AssemblyVersion’s third component to “*” and leave the fourth component blank, which will make Visual Studio set the third and fourth components automatically when the project is built. I’m going to comment out the AssemblyFileVersion too. If you do so, VS will set your file version to the same as the assembly version, giving us one less thing to deal with:
&lt;img src=&quot;https://danprice.dev/images/2020-6-12-1-assemblyinfo.png&quot; alt=&quot;assemblyinfo.cs&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Just to confirm what we’ve done (you can skip this step if you so desire), double-click &lt;em&gt;My Project&lt;/em&gt;, and in the &lt;em&gt;Application&lt;/em&gt; tab click &lt;em&gt;Assembly Information&lt;/em&gt;. You should see your assembly version set to whatever you want your major and minor revisions to be, followed by an asterisk and an empty box, like so:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://danprice.dev/images/2020-6-12-2-assemblydialog.png&quot; alt=&quot;assembly info dialog&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Click OK, save, close, we’re done.&lt;/p&gt;

&lt;h2 id=&quot;packagecodes-and-productcodes-and-upgradecodes-oh-my&quot;&gt;PackageCodes and ProductCodes and UpgradeCodes, oh my!&lt;/h2&gt;

&lt;p&gt;In addition to a simple version number, internally an MSI has a number of related fields we should look at as well. These are:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;UpgradeCode&lt;/strong&gt;
This GUID signifies what “family” of software a particular MSI is from – Windows Installer uses this to decide if there are any other versions of the same software on the system. For the purposes of this post, we’ll keep it the same.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;ProductCode&lt;/strong&gt;
This GUID identifies a particular release of a piece of software. Different versions and languages of the software should have different ProductCodes. We’ll change it in our script (below).&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;PackageCode&lt;/strong&gt;
This identifies a particular installer package – the MSI file on your disk. We’ll change it here but there are reasons you might not want to; see the link below.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For more information on these fields, pusu has written an excellent guide here: &lt;a href=&quot;https://blogs.msdn.microsoft.com/pusu/2009/06/10/what-are-upgrade-product-and-package-codes-used-for/&quot;&gt;https://blogs.msdn.microsoft.com/pusu/2009/06/10/what-are-upgrade-product-and-package-codes-used-for/&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;the-plan&quot;&gt;The Plan&lt;/h2&gt;

&lt;p&gt;So, with all that explained, what do we need to accomplish?&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;We need to get the version of the assembly and truncate it to a 3-part version number.&lt;/li&gt;
  &lt;li&gt;We need to generate a new GUID for the ProductCode.&lt;/li&gt;
  &lt;li&gt;We need to generate a new GUID for the PackageCode.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And we need to do all of this after the application is built (so the version number is current), but before the MSI is compiled. Here’s how:&lt;/p&gt;

&lt;h2 id=&quot;set-up-your-installer&quot;&gt;Set Up Your Installer&lt;/h2&gt;

&lt;p&gt;No rocket science for this bit; make sure your install project is set up: Primary output from your main project in the application folder, shortcut in the programs menu, etc.&lt;/p&gt;

&lt;h2 id=&quot;stick-to-the-script&quot;&gt;Stick To The Script!&lt;/h2&gt;

&lt;p&gt;We’re going to write a script to sort our versions and GUIDs that will run as a pre-build event of the installer, changing some variables in the project file before it’s compiled.&lt;/p&gt;

&lt;p&gt;The script is available for download from &lt;a href=&quot;https://gist.github.com/therezin/f0334cc0c09bf79e58608935dbca9bd6&quot;&gt;https://gist.github.com/therezin/f0334cc0c09bf79e58608935dbca9bd6&lt;/a&gt;. I’ve commented it pretty extensively, but we’ll go through it. If you’re not interested in the inner workings, skip this section.&lt;/p&gt;

&lt;div class=&quot;language-vb highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;explicit&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;dim&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;          &lt;span class=&quot;c1&quot;&gt;'commandline arguments&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;dim&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fso&lt;/span&gt;           &lt;span class=&quot;c1&quot;&gt;'filesystem object&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;dim&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;             &lt;span class=&quot;c1&quot;&gt;'file handle&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;dim&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fbak&lt;/span&gt;          &lt;span class=&quot;c1&quot;&gt;'backup copy of file&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;dim&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rgx&lt;/span&gt;           &lt;span class=&quot;c1&quot;&gt;'regex object&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;dim&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rMatches&lt;/span&gt;      &lt;span class=&quot;c1&quot;&gt;'regex match&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;dim&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rSubmatches&lt;/span&gt;   &lt;span class=&quot;c1&quot;&gt;'regex submatches&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;dim&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rSplit&lt;/span&gt;        &lt;span class=&quot;c1&quot;&gt;'regex submatches, split&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;dim&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vnew&lt;/span&gt;          &lt;span class=&quot;c1&quot;&gt;'rSplit, unsplit.&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;dim&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;guid&lt;/span&gt;          &lt;span class=&quot;c1&quot;&gt;'Guid object&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;dim&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;setupFilename&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;'setup project filename&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;dim&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;setupProject&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;'setup project file contents&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;First up, pretty simple: setting up the variables we’ll need later on. It’s good practice to always start your visual basic scripts with option explicit, as otherwise you can use variables without declaring them. This doesn’t sound bad, but all you have to do is mis-type a variable name and we’re into “undefined behaviour”.&lt;/p&gt;

&lt;div class=&quot;language-vb highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;'we need 2 command-line arguments. Quit if we don't have them.&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wscript&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;arguments&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;count&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;then&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wscript&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;quit&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;'read project file and rename it as a backup.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;Set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fso&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CreateObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Scripting.FileSystemObject&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;Set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fso&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OpenTextFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;setupProject&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReadAll&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Close&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;fbak&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.bak&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fso&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fileexists&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fbak&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;then&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fso&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;deletefile&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fbak&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;fso&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;movefile&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fbak&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rgx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;regexp&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;rgx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;global&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Again, not much to say here that’s not in the comments: Make sure we have 2 command-line arguments – we want the source binary and the deployment project file. Then we delete any existing backup and create a copy of the project file in case anything goes wrong.&lt;/p&gt;

&lt;div class=&quot;language-vb highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;'find binary's version number&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;vnew&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fso&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetFileVersion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;rgx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pattern&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;(\d+\.\d+\.\d+)\.\d+&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rMatches&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rgx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vnew&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;vnew&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rMatches&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;submatches&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Into the guts of it. We’re getting the file version of the compiled application binary and parsing it, putting the first 3 parts of the version number into vnew.&lt;/p&gt;

&lt;div class=&quot;language-vb highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;'find, increment and replace version number&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;rgx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pattern&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;(&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;ProductVersion&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; = &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;8:)(\d+(\.\d+)+)&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rMatches&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rgx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setupProject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;rSubmatches&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rMatches&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;submatches&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;rSplit&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rSubmatches&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;rSplit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ubound&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rSplit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rSplit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ubound&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rSplit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;setupProject&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rgx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setupProject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;$1&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vnew&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Just like the comment says, here we’re parsing the .vdproj file for the ProductVersion string and replacing it with vnew.&lt;/p&gt;

&lt;div class=&quot;language-vb highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;'replace ProductCode&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;rgx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pattern&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;(&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;ProductCode&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; = &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;8:)(\{.+\})&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;guid&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CreateObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Scriptlet.TypeLib&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Guid&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;guid&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;guid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;guid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;setupProject&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rgx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setupProject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;$1&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;guid&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;'replace PackageCode&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;rgx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pattern&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;(&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;PackageCode&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; = &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;8:)(\{.+\})&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;guid&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CreateObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Scriptlet.TypeLib&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Guid&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;guid&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;guid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;guid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;setupProject&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rgx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setupProject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;$1&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;guid&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And here, just like it says, we’re replacing the ProductCode and PackageCode with new GUIDs.&lt;/p&gt;

&lt;div class=&quot;language-vb highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;'write project file&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;setupFilename&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fso&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CreateTextfile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setupFilename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setupProject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;close&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Simple enough. Write it out.&lt;/p&gt;

&lt;h2 id=&quot;build-events&quot;&gt;Build Events&lt;/h2&gt;

&lt;p&gt;Here’s where we set it to work. Selecting our installer project in the Solution Explorer, we get these properties:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://danprice.dev/images/2020-6-12-3-properties.png&quot; alt=&quot;properties dialog&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The PreBuildEvent is the one we’re after, and hitting the ellipsis button brings us a neat little editor. “Neat”, because the event scripts have their own macros relating to the project directory and so on.&lt;/p&gt;

&lt;p&gt;Our script file is in the root of the solution directory and event scripts are referenced from the project directory, so we’ll have to go up a level. Here’s my script:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$(ProjectDir)\..\SetSetupVersion.vbs $(ProjectDir)\..\MsiDemoApp\bin\$(Configuration)\MsiDemoApp.exe $(ProjectDir)\Setup.vdproj
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The format is pretty simple – the VB script I linked above accepts 2 arguments: the name of your compiled application exe, and the deployment project file. Obviously, you’ll want to set the names of your project folder, binary and setup project file accordingly.&lt;/p&gt;

&lt;p&gt;Now we’re all set. Simply build your solution and watch as your MSI version is updated to the assembly’s and your GUIDs are replaced with new ones.&lt;/p&gt;</content><author><name></name></author><category term="Code" /><summary type="html">For many years, Visual Studio has had the option to automatically increment a project’s version number when building that project. However, deployment projects use their own version numbers and ignore the version number of the project they’re deploying. Here’s how you can automate the deployment project’s (and therefore the compiled MSI’s) version number to match the product you’re deploying.</summary></entry></feed>