(Note: The following may work with Visual SourceSafe or other version control systems, but I only tested with TFS.)
(Note 2: Some kind folks indicated I might have sounded more shrill than I probably meant so I’ve edited this post. Sorry for any offense. There’s a reason I need editors!)

Now that I’ve finished my book, I finally had a chance to sit down and move my life over to Team Foundation System (TFS). There’s a lot to like about TFS, but one major problem is it’s relatively weak (ok, lousy) support for working offline. While many developers never leave the cozy confines of their office, many of us work all over the place where we are not permanently wired into our TFS servers.

For years, I’ve never used the integrated version control with Visual Studio because, frankly, it had some issues. Nearly all the problems I’ve seen developers having with Visual C++ and Visual Studio in the past could be traced back to either using the integrated version control or .SUO bugs. While you can use TFS in VS 2005 with just the Source Control Explorer, there are major benefits to working with the integrated version control. Fortunately, Microsoft rewrote the integration from scratch so it no longer takes forever to open projects and you don’t just randomly crash any more.

However, nothing is worse than opening up a project when you don’t have your connection to TFS and getting those three dialogs telling you in order: 1) VS can’t find your TFS server, 2) the solution is under version control but its binding information can’t be found, and 3) the ultra scary dialog with the option to temporarily work uncontrolled (which sounds kind of like a negative Zen thing) or potentially mess up your project by removing the bindings permanently. I really wish VS would be a bit smarter and if it can’t find your TFS server, it just ignore the source bindings.

All of this relates to what I consider a design flaw in Visual Studio, the fact that the version control binding information is stored in the .SLN and project files. I can’t understand why this information is not in a separate file that sits beside the solution or project, especially when the *.vspscc and *.vssscc files are already there. Sadly, this is just how it is.

Therefore, I set out to see if I could come up with a way to allow better offline working. Since the solutions and projects are just text files, I knew that you can edit out the version control bindings manually and VS no longer thinks the project is under version control. Of course, taking the binding out is one thing, but I wanted to get it put back. While I guess I could have two projects, one with bindings and one without, the chance of adding a file to one of the projects and forgetting to add it to another is far too great.

What I wanted was something that would automatically remove the bindings and put them back.  The good news is that I was able to come up with something that’s working pretty well for me and thought it might benefit others as well. However, I have to issue a major caveat here: My tool changes your solutions and project files. It’s worked for me, but you’ll want to see how it works in your environment before relying on it. All the usual warnings of using code you found on “teh internets” apply.

My tool is two MSBUILD tasks, RemoveVersionControlBindingsTask and RestoreVersionControlBindingsTask, in my Wintellect.Build.Tasks.DLL assembly that’s part of my book’s updated source code. For those of you who want just the core tasks, I extracted them out into their own assembly you can get here. Both include source code.

As you can tell by the names of the tasks, the RemoveVersionControlBindingsTask task will remove the version control binding information and store it into a .BINDINGS file beside the solution or project. (Note, you can change the extension used in the task). Thus, for your HAPPY.SLN, after running the tasks you’ll have HAPPY.SLN.BINDINGS. The RestoreVersionControlBindingsTask, if you haven’t guessed, will look for the .BINDINGS file and put the version control bindings back into the file. If you want to test the tasks on something other than your real projects, the .Wintellect.Build.TasksTests directory contains the tests I used.

I’ve provided parsers that handle .SLN, .CSPROJ, .VBPROJ, and .VCPROJ files. To use the tasks, simply create a .PROJ file like the following, where you define the ProjectFiles item group.

<Project xmlns=”http://schemas.microsoft.com/developer/msbuild/2003″ >
<ProjectFiles Include=”$(StartDirectory)***.sln;
<Import Project=”.BuildsSourceBindings.Targets”/>

To remove the bindings, you’d execute the command line:
msbuild Foo.proj /t:RemoveVersionControlBindings

To restore the bindings, execute
msbuild Foo.proj /t:RestoreVersionControlBindings

If you want to write a parser for a project type that’s not included, derive your type from IBindingParser, and implement the methods and property necessary. Name your binding parser so that it ends in “BindingParser.dll,” and put it in the same directory as Wintellect.Build.Tasks.DLL to get it used. For example, if you write a .VDPROJ parser, you would call it VDProjectBindingParser.DLL. See the parsers included in Wintellect.Build.Tasks.DLL for how they work. If you do write a parser for another project type, please send me a mail (john at this web site) with the code so I can integrate it into the distribution for others to use.

After you get back from the road and you reconnect to your TFS server, it’s time to run the excellent TFS Power Toy with the Online command to update your changes. However, the Online option grinds through all your workspaces and if you use the /adds and /deletes options, you’ll be wading through massive numbers of .PDB, .OBJ, and other types of files you definitely do not want to do a pending check in on. As that was screaming to be encapsulated in a MSBuild task, I wrote the TFPowerToyOnlineTask, which is also part of Wintellect.Build.Tasks.DLL.

By default, the task does the following command options to Online: /adds, /deletes, and /recursive. For the /excludes, by default, I put on the following items to skip on the add: *.pdb, *.ncb, *.obj, *.exe, *.dll”, *.ocx, *.CodeAnalysisLog.xml, *.lastcodeanalysissucceeded, *.suo, *.user, *.orig, *.bak, *.temp, *.tmp, *.Bindings, and OBJ. (OBJ keeps any directories with OBJ in the name from being added.) You can add your own particular exclusions by adding those items to the BackOnlineExcludes item group. Finally, the only required item you specify to TFPowerToyOnlineTask is the BackOnlineDirectoriesOrFiles property where you specify directories or wild card types to process. My intent is that you’d specify the directory for the your project with this item.

<Project xmlns=”http://schemas.microsoft.com/developer/msbuild/2003″ >
<BackOnlineDirectoriesOrFiles Include=”.”/>
<Import Project=”.BuildSourceBindings.Targets”/>

To remove the bindings, you’d execute the command line:
msbuild Foo.proj /t: BackOnline

Of course, all is not perfect in TFPowerToyOnlineTask land. When I first ran my wrapper task, I expected the usual Online user interface to popup so I could manually check the files. I was quite confused when I didn’t see the Online user interface and the task output was telling me that all changes were made as pending. It was just like TFPT Online was run with the /noprompt switch. Since I definitely wasn’t turning that switch on, I was stumped. A little bit of quality time with Reflector showed that either this was either a conscious design decision or a bug in the TFPT.EXE program:

public bool IsNoPromptSpecified ( )
if ( !this.m_display.IsStdOutRedirected )
return this.m_arguments.Contains ( Options.ID.NoPrompt );
return true;

Simply redirecting output forces the /noprompt switch. That doesn’t make much sense to me. Of course, MSBuild is redirecting the output so there’s no workaround. You can see this yourself by simply running:
tfpt online > x

Now that I’ve been using TF.EXE (the command line part of TFS) for a while, I see the problem in TFPT.EXE looks to be a design decision. While there’s probably some reason behind the decision, it’s a little weird that doing a common action, redirecting output, makes the tool behave differently. Because of this issue, the TFPowerToyOnlineTask isn’t as useful as it should be. However, if you’re careful, it’s still worth using. Hopefully, Microsoft will allow us to redirect the command output (and not change the behavior of the command!) or add a /forceui switch so we can see the Online user interface no what.

I hope you’ll find these tasks useful and they help you have a better offline working experience. As always, please don’t hesitate to let me know if you have any suggestions, find any bugs, or add a binding parser for a new project type.