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>
While Option 2 is very cool and clean looking, I decided to stick with Option 1 for the following reasons:
- 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)
- Writing the Post-build (Option 1) command was much quicker
- 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