Monday, August 25, 2008

Building VSTO Projects without Visual Studio

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:

image

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:

C:\TestAddIn>msbuild TestAddIn.csproj
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
"C:\Program Files\MSBuild\Microsoft\VisualStudio\v9.0\OfficeTools\Microsoft.VisualStudio.Tools.Office.Office2007.target
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:

C:\Program Files\MSBuild\Microsoft\VisualStudio\v9.0\OfficeTools\Microsoft.VisualStudio.Tools.Office.Office2007.targets(
146,9): error MSB4062: The "VerifyClickOnceSigningSettings" task could not be loaded from the assembly Microsoft.VisualS
tudio.Tools.Applications.BuildTasks
, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a. Could not load f
ile or assembly 'Microsoft.VisualStudio.Tools.Applications.BuildTasks, Version=9.0.0.0, 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=9.0.0.0, 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.

7. Install the VSTO runtime on the build server. Since I’m using Office 2007 in this post, I used the VSTO 3 runtime. See this page for information on which runtime you require.

8. Build!

image

Yay! Similar steps can be followed for Office 2003 projects (which is what we’re using at work, actually).

7 comments:

Anonymous said...

Hi,
I have VS 2008 in my PC.
I can't find MicroSoft.VisualStudio.Tools.Office.BuildTasks.dll any where in my developer's PC but I do find MicroSoft.VisualStudio.Tools.Applications.BuildTasks.dll

Strange thing is the msbuild works iun my PC but not in build server.
I have installed VSTO 3.0 runtime.
Could you help?
Thanks
Guy

Kent Boogaart said...

Guy,

Do you have Visual Studio Tools for Office installed? http://www.microsoft.com/downloads/details.aspx?FamilyId=5E86CAB3-6FD6-4955-B979-E1676DB6B3CB&displaylang=en

HTH,
Kent

Anonymous said...

Hi,

I had the same problem. This dll only was in the GAC. You can copy it from command line going to the following directory:

cd \WINDOWS
cd \assembly
cd \GAC_MSIL
cd \Microsoft.VisualStudio.Tools.Office.BuildTasks.dll

Inside these directories there is your dll.

Regards.

Christian said...

Hi Kent,
I tried this and it works fine, but it seems it is necessary to have Word installed ... without it I get an error in the build task.

Is this true or am I missing something?

Kent Boogaart said...

@Christian: no, you certainly shouldn't need either VS or Office on the machine - that's the point of the post. There must be something wrong with your setup . . .

Christian said...

Hi Kent,

thank you for responding!

It seems that during a VSTO build task the word.dot file is copied to the output directory and therefor office is opened.

I can see those WORD instance popping up in task-manager when WORD ist installed.

I don't understand why WORD is necessary for copying.

Whithout WORD installed this happens:

Die Datei wird von "C:\Buildserver\Temp\kvws.kib.vorver\source\kvws.kib.vorver.word.2003.addin\kvws.kib.vorver.word.2003.addin\kvws.kib.vorver.word.2003.addin\kvws.kib.vorver.word.2003.addin.vorlage.dot" in "..\..\..\..\bin\kvws.kib.vorver.word.2003.addin.vorlage.dot" kopiert.
ERROR in C:\Program Files\MSBuild\Microsoft\VisualStudio\v9.0\OfficeTools\Microsoft.VisualStudio.Tools.Office.Office2003.targets(96,9) : An error occurred while attempting to persist the data. The message returned is: Failed to start Word. Ausnahme von HRESULT: 0x80040403.

Do you have any clue or a hint for me?

Kent Boogaart said...

Not sure, Christian. My scenario didn't involve any .dot files - not sure how you're using them.

All I can suggest is starting with a clean VM and following my steps in this blog post. That might give you some clue as to what's going on.