At work, I recently added a VSTO project to our solution. Predictably, the build server didn’t like it. Less predictably, I found that the installation package required to build VSTO projects also required Visual Studio to be installed:
My initial thought was that there must be another installer that does not require VS to be installed. However, after searching high and low I couldn’t find such a beast. I had an MS employee confirm this rather unsavory situation here.
Frankly, this blows, since it wastes a VS license and also causes a VS instance to spin up on every build (which is very often when you’re doing CI with multiple projects hosted on the same build server). It just makes CI far messier and costly to set up and run, which is exactly the kind of friction I’d very much prefer to avoid.
And when you think about it, it’s completely unnecessary. What is VSTO besides some combination of managed and unmanaged DLLs? Why on earth is VS required to build a VSTO project? Next you’ll tell me I need Office installed on the build server!
Thankfully, it’s not required (except by the installer), and this post details how you can set up your build server to build VSTO projects without the overhead of VS. There may be simpler ways (by tricking the installer, for example), but this is how I achieved it.
To write this post I set up a clean VPC with XP installed. I then created a VSTO project (Excel 2007 add-in) on the host machine and documented the steps necessary to build it on the guest machine. Some of these steps may not be necessary on your build machine (the first two steps in particular) but I’ve listed them here for completeness.
1. Install the .NET Framework. I used .NET 3.5 Framework for the purposes of this post.
2. Install the .NET SDK. For this post, I used the Windows SDK for 3.5 and only installed the .NET developer tools component.
Attempting to build at this point obviously yields an error:
Microsoft (R) Build Engine Version 2.0.50727.1433
[Microsoft .NET Framework, Version 2.0.50727.1433]
Copyright (C) Microsoft Corporation 2005. All rights reserved.
C:\TestAddIn\TestAddIn.csproj(180,11): error MSB4019: The imported project
s" was not found. Confirm that the path in the <Import> declaration is correct, and that the file exists on disk.
3. Copy the folder C:\Program Files\MSBuild\Microsoft\VisualStudio\v9.0\OfficeTools from a developer machine that can build the VSTO project to the same location on the build server.
Now attempting to build gives us a different set of errors along the lines of:
ThisAddIn.cs(3,17): error CS0234: The type or namespace name 'VisualStudio' does not exist in the namespace 'Microsoft'
(are you missing an assembly reference?)
ThisAddIn.Designer.cs(19,55): error CS0234: The type or namespace name 'Office' does not exist in the namespace 'Microso
ft' (are you missing an assembly reference?)
I think we can answer the question of ‘are you missing an assembly reference?’ with a resounding ‘yes!’. There are obviously a whole bunch of VSTO-related assemblies present on the developer machine that are not yet present on the build server. So how do we get them there?
We have a couple of choices. One, we could manually copy VSTO assemblies from the developer’s GAC to the build server’s GAC. Two, we could change the project such that it references a copy of the VSTO assemblies rather than referencing them in the GAC. I opted for the latter approach, because it is more self-contained.
4. Place a copy of the VSTO assemblies into a Lib (or similar) directory for your project. Reference these copies rather than those in the GAC. The easiest way to do this is to change the Copy Local property to true on the existing references, rebuild, copy the assemblies from the build output directory to the Lib directory, delete the old references, and then re-add the references by using the copies in Lib.
Now we get:
146,9): error MSB4062: The "VerifyClickOnceSigningSettings" task could not be loaded from the assembly Microsoft.VisualS
tudio.Tools.Applications.BuildTasks, Version=188.8.131.52, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a. Could not load f
ile or assembly 'Microsoft.VisualStudio.Tools.Applications.BuildTasks, Version=184.108.40.206, Culture=neutral, PublicKeyToken=
b03f5f7f11d50a3a' or one of its dependencies. The system cannot find the file specified. Confirm that the <UsingTask> de
claration is correct, and that the assembly and all its dependencies are available.
Fair enough. VSTO has some custom MSBuild tasks that the .targets file refers to, but is unable to load. If we crack open the .targets file we see declarations such as:
<UsingTask TaskName="VerifyClickOnceSigningSettings" AssemblyName="Microsoft.VisualStudio.Tools.Applications.BuildTasks, Version=220.127.116.11, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
Since AssemblyName is specified (and not AssemblyFile), a probe for the assembly is occurring same as it would for an Assembly.Load() call. To rectify the situation, we need to . . .
5. Copy the Microsoft.VisualStudio.Tools.Applications.BuildTasks assembly from the developer machine to the build machine and register it in the GAC with gacutil.exe.
6. Do the same for Microsoft.VisualStudio.Tools.Office.BuildTasks.
We’re getting close now, but there are still some missing assemblies. We could manually GAC them as per step #6, but it turns out all the remaining assemblies are part of the VSTO runtime. Therefore, we can just install the runtime.
Yay! Similar steps can be followed for Office 2003 projects (which is what we’re using at work, actually).