Category Archives: Microsoft Visual Studio

Versioning Visual Studio Solutions The Easy Way

Versioning your assemblies on each test or release build is an essential best practice to ensure identifiable releases (for testing and support) and comply with Windows Installer standards if you produce MSIs (for example using WIX).

However many developers won’t have access to a fully setup and maintained “continuous integration” build environment, which typically provide versioning support built-in. And anyway there are the interim builds you may want to make quickly on a local developer PC, for example during WIX development. They probably won’t be done via a build sever for convenience and speed.

This brings a requirement for a simple local method to quickly increment the build across all projects and various configuration files. Here is a solution involving just a PowerShell script and text file, which you can add to your solution to achieve the same. You could also integrate this with a build server, providing the best of both solutions.

Firstly, here are the files you add to the root of your solution (in “Solution Items”):

  • Version.txt   – Stores the major and minor version parts which you will manually increment according to your target product and sprint/release versions, plus the build and revision parts which are automatically set by the script to the year, month, day and build number.
  • Version.ps1 – PowerShell script which reads the old Version.txt, generates a new version number then writes it back. Then it edits all necessary project files (e.g. AssemblyInfo.cs, Global.wxi, App.config, Web.config).
  • Version.cmd – Windows Command script file which calls the PowerShell script with the necessary parameters. Makes it easy for the developer or build script to invoke the PowerShell script.

So you start by creating a Version.txt file with your major and minor versions and any build and revision, for example:

2.5.0.0

Then create the Version.ps1 containing the following script, editing or extending the final subroutine calls to cover all of your relevant project files:

Write-Output "Version"
Write-Output "======="
Write-Output "Increments the version number stored in the Version.txt file,"
Write-Output "then applies it to all relevant source files in the solution."
Write-Output "Build is set to the UTC year and month in ""yyMM"" format."
Write-Output "Revision is set to the UTC day * 1000 plus a three digit incrementing number." 
Write-Output ""

trap
{
    Write-Error $_
    exit 1
}

function Update-Version ([Version]$Version)
{
    $date = (Get-Date).ToUniversalTime()
    $newBuild = $date.ToString("yyMM")
    $dayRevisionMin = $date.Day * 1000
    if (($Version.Build -lt $newBuild) -or ($Version.Revision -lt $dayRevisionMin)) { $newRevision = $dayRevisionMin + 1 } else { $newRevision = $Version.Revision + 1 }
    New-Object -TypeName System.Version -ArgumentList $Version.Major, $Version.Minor, $newBuild, $newRevision
}

function Get-VersionFile ([String]$File)
{
    Write-Host ("Reading version file " + $File)
    $versionString = [System.IO.File]::ReadAllText($File).Trim()
    New-Object -TypeName System.Version -ArgumentList $versionString
}

function Set-VersionFile ([String]$File, [Version]$Version)
{
    Write-Host ("Writing version file " + $File)
    [System.IO.File]::WriteAllText($File, $Version.ToString())
}

function Set-VersionInAssemblyInfo ([String]$File, [Version]$Version)
{
    Write-Host ("Setting version in assembly info file " + $File)
    $contents = [System.IO.File]::ReadAllText($File)
    $contents = [RegEx]::Replace($contents, "(AssemblyVersion\("")(?:\d+\.\d+\.\d+\.\d+)(""\))", ("`${1}" + $Version.ToString() + "`${2}"))
    $contents = [RegEx]::Replace($contents, "(AssemblyFileVersion\("")(?:\d+\.\d+\.\d+\.\d+)(""\))", ("`${1}" + $Version.ToString() + "`${2}"))
    [System.IO.File]::WriteAllText($File, $contents)
}

function Set-VersionInWixGlobal ([String]$File, [Version]$Version)
{
    Write-Host ("Setting version in WIX global file " + $File)
    $contents = [System.IO.File]::ReadAllText($File)
    $contents = [RegEx]::Replace($contents, "(\<\?define\s*ProductVersion\s*=\s*"")(?:\d+\.\d+\.\d+\.\d+)(""\s*\?\>)", ("`${1}" + $Version.ToString() + "`${2}"))
    [System.IO.File]::WriteAllText($File, $contents)
}

function Set-VersionInAssemblyReference ([String]$File, [String]$AssemblyName, [Version]$Version)
{
    Write-Host ("Setting version in assembly references of " + $File)
    $contents = [System.IO.File]::ReadAllText($File)
    $contents = [RegEx]::Replace($contents, "(["">](?:\S+,\s+){0,1}" + $AssemblyName + ",\s+Version=)(?:\d+\.\d+\.\d+\.\d+)([,""<])", ("`${1}" + $Version.ToString() + "`${2}"))
    [System.IO.File]::WriteAllText($File, $contents)
}

function Set-VersionInBindingRedirect ([String]$File, [String]$AssemblyName, [Version]$Version)
{
    Write-Host ("Setting version in binding redirects of " + $File)
    $contents = [System.IO.File]::ReadAllText($File)
    $oldVersionMax = New-Object -TypeName "System.Version" -ArgumentList $Version.Major, $Version.Minor, $Version.Build, ($Version.Revision - 1)
    $pattern = "([\s\S]*?<assemblyIdentity\s+name=""" + $AssemblyName + """[\s\S]+?/>[\s\S]*?<bindingRedirect\s+oldVersion=""\d+\.\d+\.\d+\.\d+-)(?:\d+\.\d+\.\d+\.\d+)(""\s+newVersion="")(?:\d+\.\d+\.\d+\.\d+)(""[\s\S]*?/>)"
    $contents = [RegEx]::Replace($contents, $pattern, ("`${1}" + $oldVersionMax.ToString() + "`${2}" + $Version.ToString() + "`${3}"))
    [System.IO.File]::WriteAllText($File, $contents)
}

$scriptDirectory =  [System.IO.Path]::GetDirectoryName($MyInvocation.MyCommand.Definition)

$versionFilePath = $scriptDirectory + "\Version.txt"
$version = Get-VersionFile -File $versionFilePath
Write-Host ("Old Version: " + $version.ToString())

$newVersion = Update-Version -Version $version
Write-Host ("New Version: " + $newVersion.ToString())
Set-VersionFile -File $versionFilePath -Version $newVersion

$otherVersionFilePath = $scriptDirectory + "\Dependencies\Other.Component.Version.txt"
$otherVersion = Get-VersionFile -File $otherVersionFilePath
Write-Host ("Other Component Reference Version: " + $otherVersion.ToString())

Set-VersionInAssemblyInfo -File ($scriptDirectory + "\MyProduct.Assembly1\Properties\AssemblyInfo.cs") -Version $newVersion
Set-VersionInAssemblyInfo -File ($scriptDirectory + "\MyProduct.Assembly2\Properties\AssemblyInfo.cs") -Version $newVersion
Set-VersionInAssemblyInfo -File ($scriptDirectory + "\MyProduct.Assembly3\Properties\AssemblyInfo.cs") -Version $newVersion
Set-VersionInAssemblyReference -File ($scriptDirectory + "\MyProduct.Assembly3\App.config") -AssemblyName "MyProduct.Assembly2" -Version $newVersion
Set-VersionInAssemblyInfo -File ($scriptDirectory + "\MyProduct.Assembly4\Properties\AssemblyInfo.cs") -Version $newVersion
Set-VersionInBindingRedirect -File ($scriptDirectory + "\MyProduct.Assembly4\Web.config") -AssemblyName "Other.Component" -Version $otherVersion
Set-VersionInWixGlobal -File ($scriptDirectory + "\MyProduct.Setup\Global.wxi") -Version $versionString

exit 0

Lastly the Version.cmd invokes the PowerShell Version.ps1 correctly from the command line, e.g. I usually open a command prompt using the Visual Studio Power Tools toolbox menu item then run either my build script (which calls Version.cmd) or just Version.cmd to increment the version alone.

@powershell -File "%~dp0Version.ps1"

Most of this script remains static. The functions provided in this example will edit the “AssemblyVersion” attribute in an “AssemblyInfo.cs” or a WIX “ProductVersion” variable in a WIX file (typically stored in a shared include file named Global.wxi). Finally the last lines of the script are customized for your actual solution, calling the relevant functions to update the version number in files of your solution.

Now each time you build your solution you have an identifiable version which is exactly same across all assemblies and even the MSI. For example, the third build of the above solution on the 23rd April 2013 will produce the following version:

2.5.1303.23003

This is broken down as follows:

  • 2.5 comes from the static major and minor version you typed in the Version.txt.
  • 1303 comes from the year and month, 2013 and 03 for March.
  • 23003 is the day, the 23rd, multiplied by 1000 to give us three predictable digits for the build number, 003 is the third build on this day, with up to 999 possible before clashing with builds on the next day (should never occur then).

Following this pattern, you could add the version files to every solution you create and maintain it simply by adding a line or two each time you add projects and calling it from any build script or system you may have. If you have new types of files to edit add your own functions and call them at the end. The general solution of using regular expression replacements should suffice for most use cases. Other more complex requirements could use an XPath query to edit configuration files where it is not possible to identify the version by string patterns alone (when a structured XML path is more appropriate to accurately locate the version entries).

I like this solution because it allows you to use best practice versioning without the overhead. And even if you do have a proper continuous build system in place, using this in place of any proprietary versioning add-on or Visual Studio extension is much simpler, flexible (e.g. use offline or when build server is out of service)  and easy to maintain, reducing your 3rd party product dependencies.

Scorching Source Code

After much experience of the issues related to switching between different PCs with Visual Studio source bound solutions, I was pleased to find the “scorch” feature of Visual Studio Team Foundation Power Tools.

Previously Visual Studio just didn’t clean itself up properly. This would leave duplicate and extra files when deletes or renames occurred on other PCs or by other users. The next developer could then mistakenly open or edit the wrong file, potentially losing changes without knowing it. Knowledge of such issues discourages developers from making proactive refactoring, e.g. for readability, consistency and reduced maintenance costs.

Another major issue was the failure of Visual Studio to update local files even when the current copy was out of date or missing. The only solution to this was to “Get Specific Version” with the option to get files even when they should exist. An assumption is apparently programmed into Visual Studio to only use the last known source control status, not bothering to check the actual files.

The SCORCH command is a combination of the TREECLEAN command which deletes extra files, plus verification that all local files are the correct version. Deleting extra files will also eliminate any compiled object, binary, test results and other outputs. This significantly reduces the size of your local source copy in case you want to take a backup or have limited disk space. Removing intermediary files can help avoid issues when shared components are updated but a local cache of older versions still exist.

With all these benefits, I’d strongly recommend that “scorching your source” (as I like to call it) should be a best practice performed regularly by developers whether in a team or stand-alone switching between different PCs. It is especially important to do before going away to work offline, checking-in offline changes, coming back from holiday or any other situation where major changes to the dependencies or structure of the source may have occurred.

Here’s how you do it:

  1. Make sure you installed the Team Foundation Power Tools. Best place to do this is via the “Tools” – “Extension Manager” (NuGet) or directly here:
    http://visualstudiogallery.msdn.microsoft.com/c255a1e4-04ba-4f68-8f4e-cd473d6b971f
  2. Open a command prompt.
  3. Enter the command “tfpt scorch <path> /recursive /diff” where “<path>” is the root directory of your source collection.
  4. Wait for it to scan your source tree, review changes (but no extra effort is usually required), then confirm to clean-up and update your local files.
  5. Repeat the command for any other collections/source root paths you have from your Team Foundation Server, e.g. sometimes one for each customer or business subsidiary.
  6. Run “tfpt scorch /?” for further options. It can be automated, although you shouldn’t do that unless you also go online/shelve/check-in any unsaved changes first.

Reasons to run Visual Studio 2010 as Administrator (UAC issues)

Unfortunately Visual Studio 2010 (even SP1 level) is still not fully prepared for Vista and Windows 7 UAC (User Account Control – basically where the Administrators group membership is ignored until you “elevate”).

Here is my list of why not, which you can use in case you need to justify it:

  1. First open of solution with IIS web applications bound to real local IIS web sites (not using the Visual Studio development server) – tells you to run as administrator because it can’t register the new projects with IIS as a normal user.
  2. WCF in IIS Web Site – cannot launch debugging of web services running below a normal web site path (HTTP/S with IIS compatibility) because it will fail to register HTTP URL security.
  3. Event Log Component Registration – fails to auto-register event source name in registry the first time you build and run a solution with an event log component, e.g. Windows Service.
  4. Help Library Manager – cannot change all settings and modify content, not even update!
  5. Azure – cannot debug solutions locally using the Azure Compute Emulator.
  6. Team Foundation Server – cannot create a new TFS team project because it tells you must run as administrator to configure Reporting Services.

Reasons NOT to run as Administrator are:

  1. Developers must get used to creating applications following the “least privilege” paradigm. The best place to do this is during creation of the application itself rather than ending up with a delay, because you have to re-design your GUIs and setup routines after integration test failures. From the user perspective that looks pretty dumb too.
  2. Compiling and running sample code downloaded from the internet (could be a Trojan). But this has a low chance, if you compile and run code from the internet and don’t look at it first it could still read your files and post via a web service without administrator privilege.
  3. Developing end-user GUIs or setup routines (e.g. WIX Custom Actions) which work with system resources or protected configuration data (e.g. IIS web site list in 2008 R2), testing that your code works with UAC elevation.

I guess Visual Studio 2012 will have more improvements in this area. But we are still waiting for a native 64bit Visual Studio so I think this list may live a bit longer. :-/

One alternative would be to mark project build events, builds, runs  or design as requiring Administrator privilege. Then most cases just the build elevates and you can still test the rest of the program at lower privilege. Only down side is it could get annoying with pop-ups. Automated builds should be okay because they normally run as fairly high privilege service accounts on servers without UAC.

I will update this list if I encounter more limitations. But I have to continue with some solutions as Administrator to get the jobs done so I may not encounter them. Suggestions welcome.

XML Serialization Assembly Generation With Visual Studio

The built-in option to “generate XML serialization assembly” in Visual Studio does not work. It only works for SOAP web proxies and this seems to have been hard-coded by Microsoft and they are not changing it. The open Microsoft Connect bug reports have been outstanding since Visual Studio 2008 was released and still exists in 2010.

Here is how to workaround the problem. Other people just call SGEN in the project post-build commands, but that does not flag the result as project output. A better way is to edit the project file in Notepad and add a call to the SGen MSBuild task in the “AfterBuild” task, as follows:-

<!—Call XML serialization assembly generation task after build because built-in Visual Studio option does not work –>
<Target Name="AfterBuild" DependsOnTargets="GenSerializationAssembly" />
<!– XML Serialization Assembly Generation –>
<Target Name="GenSerializationAssembly"
        DependsOnTargets="AssignTargetPaths;Compile;ResolveKeySource"
        Inputs="$(MSBuildAllProjects);@(IntermediateAssembly)"
        Outputs="$(OutDir)$(_SGenDllName)">
  <SGen BuildAssemblyName="$(TargetFileName)"
        BuildAssemblyPath="$(OutDir)"
        References="@(ReferencePath)"
        ShouldGenerateSerializer="true"
        UseProxyTypes="false"
        KeyContainer="$(KeyContainerName)"
        KeyFile="$(KeyOriginatorFile)"
        DelaySign="$(DelaySign)"
        ToolPath="$(SGenToolPath)">
    <Output TaskParameter="SerializationAssembly" ItemName="SerializationAssembly" />
  </SGen>
</Target>

Updated Ports Required For Remote Debugging With Visual Studio 2008 SP1

I recently discovered TCP port 2922 is also required to successfully setup a remote debug session from Visual Studio 2008 SP1 to a Windows 2003 machine. According to the Wizard and documentation this port is not mentioned, and searches on the internet describe it as “CESD Contents Delivery Data Transfer”. Normally I would expect to find this documented somewhere in a Microsoft KB article including this port.

The complete list of ports on the Visual Studio (local) side is hence now:-

Visual Studio Side / Local

  • DCOM – TCP 135
  • IKE NAT Traversal – UDP 4500
  • IKE NAT – UDP 500
  • CESD – TCP 2922

The remote side also requires TCP 445, UDP 137, and UDP 138. Perhaps also 2922, but I cannot be sure because the firewall was not configured in the test environment I had to work in.

The official Microsoft documentation (without port 2922) is here: http://msdn.microsoft.com/en-us/library/bb385831.aspx