Copying Files After a Successful Build With Robocopy on VS2010

The Problem
In one of the latest projects I’ve been working on, I needed to be able to interchange data providers. We decided to use Prism and MEF to load up the concrete instance of our IDataProvider at runtime, and created a separate project to contain the XML based implementation of that provider. Since we need a concrete instance to debug I wanted the latest .dll and .pdb of the XML data provider project to be copied into a Modules folder in the app’s output directory after it has successfully built. At first I thought I would use xcopy to copy the files, but after seeing that it has been deprecated, I switched to using Robocopy (which turned out to be just as easy). Once I figured what tool I would use to copy the files, I needed to figure out where to run the command from and I found 2 options.

Option 1: Project Post-build event
The quickest option I found was to enter the Robocopy command into the Post-build Event Command Line box in the project properties. Since Robocopy comes with Windows Vista and on you can open the Command Prompt and type robocopy /? to see everything that it is capable of. The command I used to copy the .dll and .pdb files was the following:
robocopy “$(TargetDir)\” “$(SolutionDir)\Application\$(OutDir)\Modules” $(TargetName).dll $(TargetName).pdb

With that command entered, I saved and then built the project, but the build was not successful and displayed an error staying robocopy exited with a code of 1. Wondering why I received this error, I went to the application’s output directory to find, to my surprise, that the Modules folder had been created and both the .dll and .pdb files had been copied successfully to that folder. Searching around for an answer I found this stackoverflow post explaining that unlike every other command line tool that returns a 0 when successful (which is what Visual Studio expects), robocopy returns a 1 when successful. So after adding a check to exit with a code of 0 if the error level was 1, it then built successfully.

Option 2: MSBuild Extension AfterBuild
On the stackoverflow post, mentioned above, there was an answer that recommended using the Robocopy task in the MSBuild Extension Pack as an AfterBuild target. I found this idea very intriguing (especially since it appeared to handle the return code of 1 issue) and attempted to use it to accomplish the same copying task. After a few hours of working with it I found that I could only get it functioning installing the MSBuild Extension Pack3.5 (even though I am on an x64 machine and building a .Net 4.0 project). I then edited my .csproj file and added the following code, which does the exact same thing as the post-build command, at the bottom of the file. After reloading and building the project, the files were copied to the Modules folder.

<Import Project="$(MSBuildExtensionsPath)\ExtensionPack\MSBuild.ExtensionPack.tasks"/>
<Target Name="AfterBuild">
	<ItemGroup>
		<OutputFiles Include="$(TargetName).dll"/>
		<OutputFiles Include="$(TargetName).pdb"/>
	</ItemGroup>
	<MSBuild.ExtensionPack.FileSystem.RoboCopy Source="$(TargetDir)\" Destination="$(SolutionDir)\Application\$(OutDir)\Modules" Files="@(OutputFiles)">
		<Output TaskParameter="ExitCode" PropertyName="Exit" />
		<Output TaskParameter="ReturnCode" PropertyName="Return" />
	</MSBuild.ExtensionPack.FileSystem.RoboCopy>
	<Message Text="ExitCode = $(Exit)"/>
	<Message Text="ReturnCode = $(Return)"/>
</Target>

The Decision
While Option 2 is very cool and clean looking, I decided to stick with Option 1 for the following reasons:

  1. Option 2 would require all the developers on the project to install the MSBuild Extension pack (not difficult at all but time consuming and annoying if your builds are failing and you didn’t know you needed the 3rd party pack)
  2. Writing the Post-build (Option 1) command was much quicker
  3. If any other developer is wondering how the files are being copied into the Modules folder (or needs to change it), with Option 1 they can quickly and easily look at (and change) the Post-build event in the properties window, but with Option 2 they have to know to go edit the .csproj file and then search it to find the AfterBuild target code

Remote Debugging from Visual Studio 2010

In my latest project I was running into an issue where running my installed application on a test machine was crashing before it could even startup, but it ran perfectly when installed on my machine. I was almost to the point of taking the time to install Visual Studio 2010 on the test machine, when my coworker, Bryan Coon, reminded me of the Remote Debugging capabilities of VS and recommended trying that first.

I started off using Danny Warren’s Remote Debugging post to get both my machine(on domain) and the test machine(off domain) enabled so that I could Attach to Process (Ctrl+Alt+P) from VS 2010 on my machine. Here are the main points that I gathered from it:

  • 3 instances of the same username needed to exist as administrators (and have been logged into at least once)
    1. The domain username on my machine (domain\travis). This is the username that Visual Studio is run under.
    2. The machine username on my machine (MyMachine\travis)
    3. The machine username on the test machine (TestMachine\travis). This is the username that the remote application is run under.
  • The Visual Studio 2010 Remote Debugging Monitor needed to be installed on the test machine
    • Use the setup program that matches the OS (x86, x64, ia64)
    • I ran the application instead of setting it up as a service, so I needed to make sure it ran as an administrator
  • Everything from the bin\Debug folder of the application must be copied to the test machine.

Once the Remote Debugging Monitor was started, I was able to view all the running processes in VS by entering the name of the server that the Remote Debugging Monitor started (travis@TestMachine) into the Qualifier box.

In most cases this would have allowed me to then attach to my running application and hit debug points. My application, however, crashed before the application even became available in the list of available processes.

So the next step was to get Visual Studio 2010 configured on my machine to start the application on the remote (test) machine when I started the application with debugging (F5). I found what was needed here. All that was required was to modify the following properties in the Debug section of the application properties screen:

  • Select Start external program and specify the path to the application on the remote computer
  • Check Use remote machine and specify the remote machine

The gotcha I ran into was that again the remote machine name is the server name that the Remote Debugging Monitor started not the actual machine name.

With these final steps completed I was able to start the application with debugging and immediately found my issue. While this process took a little bit of time to setup, it saved a bunch of time that would have been spent installing Visual Studio 2010 and allowed me to keep the test machine clean of all developer tools. So if I run into an issue like this again I will definitely think to try Remote Debugging right away.

Installers Woes–Probing Paths

Today for RECESS I was working on Informant. I need a single installer to lay down my Windows Service and WPF application. I’ve done this once before in VS2008, so I figured it wouldn’t be too hard to do in VS2010. I did not anticipate any breaking changes between the two versions however, in VS2010 the way that dependencies are tracked is different than in VS2008. I have two projects that have a shared assembly but in VS2010 the dependency is injected only once for the first project output that claims it. My coworker Dan Hanan introduced me to Probing Paths. This allowed me to have the WPF application in the Application Folder of the installer and the Windows Service application in a folder named Service as a sibling to the WPF Application. In the WPF App.config I added the following probing path:


<runtime>
	<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
		<probing privatePath="Service"/>
	</assemblyBinding>
</runtime>

This allowed me to to have the common assembly dependency in the Service folder and still allow the WPF application to access it. This way the dependency is injected only once to the project installer, but used multiple times.