Introduction
This article describes how to configure a VS 2003 or 2005 project to automatically GAC an assembly after a build. It also describes how to move the debug information into the GAC.
Background
I first used this technique a couple of years ago when working on projects with quite a bit of shared code. Most of the framework assemblies were put into the GAC for the projects to share. This became a useful technique for debugging the project in the same manner in which it would be deployed to production.
In a project file you can automatically GAC an assembly after a build by putting the following in the Post-Build event.
"C:\Program files\Microsoft Visual Studio 8\SDK\v2.0\Bin\GacUtil.exe"
-i "$(TargetPath)"
Obviously if you're in a team environment, you'll probably want to use environment variables like %ProgramFiles%
. To the best of my knowledge there is no environment variable pointing to the full path. If you open a VS command prompt this directory is added to the path variable, but oddly that's not available during a build.
Note: for 2003 the path is C:\Program Files\Microsoft Visual Studio .NET 2003\SDK\v1.1\Bin. After adding this, you need to address the opposite side of the problem: Ungacing before a build. In the pre-build event put:
"C:\Program files\Microsoft Visual Studio 8\SDK\v2.0\Bin\GacUtil.exe"
-u $(TargetName)
Most of you could have probably figured this out on your own, if you haven't already. The problem I ran into is that once I had gaced an assembly, the debug info didn't go with it, no matter how I built the assembly. This is where things get sortof weird. The GAC is actually a top secret series of folders under the windows\assembly folder. If you pull this folder up in Explorer, you get a "special" window that allows you to perform GAC administration (e.g. drag and drop install, delete, etc.). If you open a command prompt and go to the same directory you'll see something like this:
Volume in drive C has no label.
Volume Serial Number is D43F-9977
Directory of C:\WINDOWS\assembly
10/06/2006 01:22 PM <DIR> GAC
10/16/2006 04:25 PM <DIR> GAC_32
11/20/2006 01:42 PM <DIR> GAC_MSIL
07/11/2006 09:44 AM <DIR> NativeImages1_v1.1.4322
10/19/2006 05:07 PM <DIR> NativeImages_v2.0.50727_32
11/20/2006 01:42 PM <DIR> temp
11/20/2006 01:42 PM <DIR> tmp
0 File(s) 0 bytes
7 Dir(s) 11,621,462,016 bytes free
This is the super-secret GAC area where things actually go. The GAC folder is for .NET 1.1 assemblies, the GAC_MSIL is (mostly) for .NET 2.0 assemblies. If you go under the GAC_MSIL folder ("cd GAC_MSIL
" for those too young to remember DOS commands) you'll see a series of directories with the same names as the assemblies in your GAC (at least the 2.0 assemblies) As an example we'll play with System.Xml. enter the System.Xml directory ("cd System.Xml
") and you'll see this:
Volume in drive C has no label.
Volume Serial Number is D43F-9977
Directory of C:\WINDOWS\assembly\GAC_MSIL\System.Xml
10/16/2006 04:24 PM <DIR> .
10/16/2006 04:24 PM <DIR> ..
10/16/2006 04:24 PM <DIR> 2.0.0.0__b77a5c561934e089
0 File(s) 0 bytes
3 Dir(s) 11,621,388,288 bytes free
This is how multiple versions of the same assembly can be in the GAC at the same time. The folder name is constructed as follows: %VersionInfo%_%CultureInfo%_%PublicKeyToken%. Therefore if you've gaced 1.0 and 1.1 of an assembly, there will be two folders here. I assume the token is there in case two vendors put out assemblies with the same name, but I'm guessing here. Moving on. Going into this folder you'll find your long lost assembly hidden away.
Volume in drive C has no label.
Volume Serial Number is D43F-9977
Directory of C:\WINDOWS\assembly\GAC_MSIL\System.Xml\2.0.0.0__b77a5c561934e089
10/16/2006 04:24 PM <DIR> .
10/16/2006 04:24 PM <DIR> ..
10/16/2006 04:24 PM 2,035,712 System.XML.dll
1 File(s) 2,035,712 bytes
2 Dir(s) 11,621,388,288 bytes free
If you want debug info for your gaced assembly, the PDB needs to go here. And there's no automatic way to get it here. I generally try to keep pre and post build events fairly generic, but in this case I don't know how to do it without some sort of helper program. If you put the following command in your post build event you can copy the PDB here:
copy "$(TargetDir)$(TargetName).pdb" "C:\windows\assembly\GAC_MSIL
\$AssemblyName$\$VersionNumber$__$PublicKeyToken$\"
Replacing $AssemblyName$
with your assembly name, etc. (The $()
macros are actual macros understood by msbuild, don't change those). The $AssemblyName$
, $VersionNumber$
, and $PublicKeyToken$
variables are NOT macros. You need to explicitly define these or create a helper program to discover them. I have in the past written a little command line utility to parse assemblies information and put their debug information into the GAC. That's a little beyond the scope of this post, but I might cover it later. Here's the gotchas. Microsoft apparently did not want you messing with these directories directly, so they've protected them. You "cannot" access them in any way except the command line. This means you can't whip out a little vbscript file to do it, you can't do it in C#, you can "only" do it through the command line or a .bat file. (Believe me, I've tried). So if you try to automate this, the best you can do is to write out a batch file then run it. Sortof clumsy, but it works. I've found that this method doesn't work so well if you want to build and GAC more than one version of an assembly. In this instance I did away with the prebuild event (unregister) and changed the post-build event to:
"C:\Program files\Microsoft Visual Studio 8\SDK\v2.0\Bin\GacUtil.exe"
-i "$(TargetPath)" /f
Which means force a replacement if there is an existing version in the GAC. This allowed me to build 2.1 without removing 2.0 (just in case anything depended on 2.0).