But it works on my PC!

The random thoughts of Richard Fennell on technology and software development

TF254024 error when upgrading TFS 2010 Beta2 to RC

Whilst upgrading a single server instance of TFS 2010 Beta2 to the RC I got a TF254024 error. This occurred at the point in the upgrade wizard where it tries to list the databases available to upgrade.

The reason for the error was the account I was logged in as (the domain administrator in my case) did not have rights on the SQL instance to see any TFS DBs. Once this was sorted, by granting owner rights to all the TFS DBs, all was OK.

I think it had got into this state as this was a ‘standard’ TFS install using an automatically installed SQLExpress instance; so rights had not been explicitly assigned during the TFS setup. By installing SQL 2008 Management Studio Express and logging in as the servers local administrator I was able to grant the rights needed.

VHD boot and c00002e2 Errors

For some reason that is beyond me now I did not setup my Lab Manager test system to be a VHD boot. So before installing the 2010 RC version I decided to P2V this system (on the same hardware) to make backups easier whilst testing. All seemed to go well

  1. I used IMAGEX to create a WIM of the disk
  2. Created an empty VHD
  3. Used IMAGEX to apply the WIM to the VHD
  4. Formatted the PC with a default Windows 7 install
  5. Added a VHD boot Windows Server 2008R2 to the PC, tested this all booted OK
  6. Replaced the test VHD with my own and rebooted

…. and it just went into a reboot cycle. Pressing F8 and stopping the reboot on error I saw I had a “c00002e2 Directory Services could not start” error. I managed to get into the PC by pressing F8 and using the AD recovery mode (safe mode did not work). After much fiddling around I eventually noticed that my boot drive was drive D: not C: as I would have expected. My VHD and parent drive had reversed letter assignments. So when the AD services tried to start they look on the parent Windows 7 partition (C:) for their data and hence failed.

I think the root cause was the way I had attached the empty VHD to used IMAGEX. I had not done it using WINPE, but just created in my Windows 7 instance and attached the VHD as drive D: before copying on the WIM

So my revised method was

  1. I used IMAGEX to create a WIM of the disk (actually used the one I already had as there was nothing wrong with it, which was a good job as I had formatted the disk)
  2. Formatted the PC with a default Windows 7 install
  3. Added a VHD boot Windows Server 2008R2 to the PC, tested this all booted OK
  4. Copied my WIM file to the same directory as my newly created W2k8R2.VHD
  5. Copied IMAGEX to this directory
  6. Booted of a Win7 DVD
  7. Pressed Shift F10 to get a prompt at the first opportunity
    1. Ran DISKPART
    2. Select Disk 1
    3. Select Part 1
    4. Detail Part – this was the 100Mb system partition Windows 7 creates and was assigned as drive C: (note when you boots Windows 7 the drive letters get reassigned just to confuse you, as to look at this you would expect your Windows 7 boot drive to be D:)
    5. Assign Letter = Q – this set the system partition to be drive Q, but any unused letter would do
    6. Select vdisk file:d:\vhd\w2k8r2.vhd
    7. attach vdisk – this loaded the VHD and assigned it the letter C: as this was now not in use
    8. list disk
    9. Select disk 2
    10. Select Part 1
    11. detail Part – checked the drive letter was correct
    12. I then exited DISKPART and from the same command prompt ran IMAGEX to put the WIM on this new drive C:
  8. Rebooted and it worked

So the technical tip is make sure your drive letter assignments are what you think they are, it may not be as obvious as you expect.

NEBytes last night

Hope everyone enjoyed my session VS2010 at NEBytes last night. I don't now about you but I think that quick end to end demo of build, manual test and Intellitrace debug work very nicely. That was the first time I have done it as a single demo and I think it works better than three smaller ones. Truely shows the intergrated team store for VS2010

Anyway as the session was demo lead there are no slides to download, but if you have follow up questions post a comment on this post or email me 


Cannot access TFS over HTTPS after upgrade from 2010 Beta 2 to RC

I upgraded our Beta2 2010 TFS last week, and after a quick local test all appeared to be working OK, so I rushed out the office like you do. However, whilst I have been out the office it was spotted that though it was working within the office using HTTP and the NETBIOS\Domain server name (TFSSERVER) it could not be accessed outside the firewall or over HTTPS inside the office, so https://tfsserver.mydomain.com:8443/tfs did not work

Turns out the problem was twofold, both it seems caused by the TFS in place upgrade process.

IIS7 Bindings

The 2010 upgrade configuration wizard appeared to removed the non Port 8080 bindings on the IIS7 TFS site instance. I had to re-add the binding on 8443 that we use to access the TFS web services (this did not happen with the related SharePoint web site, this was still happily on ports 80 and 443)


One this was re-added I could access the server from the console on port 8443 using the Url https://tfsserver.mydomain.com:8443/tfs, however other clients still could not access it

Windows Firewall with Advanced Security

One the Firewall I noticed that though the rule to allow port 8443 was there it’s profile was set to only public/private (can’t remember how it used to be set).

image image

Once I added domain (or set it to any) I was able to access the upgraded TFS server from other clients using the Url https://tfsserver.mydomain.com:8443/tfs

Lessons learnt building a custom activity to run Typemock Isolator in VS2010 Team Build

Updated 25th March 2010 - All the source is now available at the Typemock Add-in site 
Updated 2nd July 2010 - Some usage notes posted
Updated 26th July 2011 - More usage notes
Updated 21st Nov 2011 - Typemock Isolator now has direct support for TFS 2010 Build, see usage notes 

I have previously posted on how you can run Typemock Isolator based tests within a VS2010 using the InvokeMethod activity. After this post Gian Maria Ricci, a fellow Team System MVP suggested it would be better to put this functionality in a custom code activity, and provided the basis of the solution. I have taken this base sample and worked it up to be a functional activity, and boy have I learnt a few things doing it.

Getting the custom activity into a team build

Coding up a custom Team Build activity is not easy, there are a good few posts on the subject (Jim Lamb’s is a good place to start). The problem is not writing the code but getting the activity into the VS toolbox. All documentation gives basically the same complex manual process, there is no way of avoiding it. Hopefully this will be addressed in a future release of Visual Studio. But for now the basic process is this:

  1. Create a Class Library project in your language of choice
  2. Code up your activity inheriting it from the CodeActivity<T> class
  3. Branch the build workflow, that you wish to use for testing, into the folder of the class library project
  4. Add the build workflow’s .XAML file to the class library project then set it’s properties: “build action” to none and “copy to output directory” to do not copy
  5. Open the .XAML file (in VS2010), the new activity should appear in the toolbox, it can be dropped onto the workflow. Set the properties required.
  6. Check in the file .XAML file
  7. Merge the .XAML file to the original location, if you get conflicts simply tell merge to use the new version discarding the original version, so effectively overwriting the original version with the version edited in the project.
  8. Check in the merged original .XAML file that now contains the modifications.
  9. Take the .DLL containing the new activity and place it in a folder under source control (usually under the BuildProcessTemplates folder)
  10. Set the Build Controller’s custom assemblies path to point to this folder (so your custom activity can be loaded) 

  11. Run the build and all should be fine

But of course is wasn’t. I kept getting the error when I ran a build

TF215097: An error occurred while initializing a build for build definition \Typemock Test\BuildTest Branch: Cannot create unknown type '{clr-namespace:TypemockBuildActivity}ExternalTestRunner'.

This was because I had not followed the procedure correctly. I had tried to be clever. Instead of step 6 and onwards I had had an idea. I created a new build that referenced the branched copy of the .XAML file in the class library project directly. I thought this would save me a good deal of tedious merging while I was debugged my process. It did do this but introduced other issues

The problem was when I inspected the .XAML in my trusty copy of Notepad, I saw that there was no namespace declared for my assembly (as the TF21509 error suggested). If I looked at the actual activity call in the file it was declared as <local:ExternalTestRunner  …… />, the local: replacing the namespace reference I would expect. This is obviously down to the way I was editing the .XAML file in the VS2010.

The fix is easy, using Notepad I added a namespace declaration to the Activity block

<Activity ……    xmlns:t="clr-namespace:TypemockBuildActivity;assembly=TypemockBuildActivity" >

and then edited the references from local: to t: (the alias for my namespace) for any classes called from the custom assembly e.g.

<t:ExternalTestRunner ResultsFileRoot="{x:Null}" BuildNumber="[BuildDetail.Uri.ToString()]" Flavor="[platformConfiguration.Configuration]" sap:VirtualizedContainerService.HintSize="200,22" MsTestExecutable="C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\MSTest.exe" Platform="[platformConfiguration.Platform]" ProjectCollection="http://typhoon:8080/tfs/DefaultCollection" Result="[ExternalTestRunnerResult]" ResultsFile="ExternalTestRunner.Trx" SearchPathRoot="[outputDirectory]" TeamProjectName="[BuildDetail.TeamProject]" TestAssemblyNames="[testAssemblies.ToArray()]" TestRunnerExecutable="C:\Program Files (x86)\Typemock\Isolator\6.0\TMockRunner.exe" TestSettings="[localTestSettings]" />

Once this was done I could use my custom activity in a Team Build, though I had to make this manual edit every time I edited the branched .XAML file in VS2010 IDE. So I had swapped repeated merges with repeated editing, you take your view as to which is worst.

So what is in my Typemock external test runner custom activity?

The activity is basically the same as the one suggest by Gian Maria, it takes all the same parameters as the MSTest team build activity and then executes the TMockRunner to wrapper MSTest. What I have done is add a couple of parameters that were missing in the original sample and also added some more error traps and logging.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Activities;
using System.IO;
using System.Diagnostics;
using Microsoft.TeamFoundation.Build.Workflow.Activities;
using Microsoft.TeamFoundation.Build.Client;
using System.Text.RegularExpressions;
namespace TypemockBuildActivity
    public enum ExternalTestRunnerReturnCode { Unknown =0 , NotRun, Passed, Failed };
    public sealed class ExternalTestRunner : CodeActivity<ExternalTestRunnerReturnCode>
        // Define an activity input argument of type string  
        /// <summary>
        /// The name of the wrapper application, usually tmockrunner.exe
        /// </summary>
        public InArgument<string> TestRunnerExecutable { get; set; }
        /// <summary>
        /// The name of the application that actually runs the test, defaults to MSTest.exe if not set
        /// </summary>
        public InArgument<string> MsTestExecutable { get; set; }
        /// <summary>
        /// The project collection to publish to e.g. http://tfs2010:8080/tfs/DefaultCollection
        /// </summary>
        public InArgument<string> ProjectCollection { get; set; }
        /// <summary>
        /// The build ID to to publish to e.g. vstfs:///Build/Build/91
        /// </summary>
        public InArgument<string> BuildNumber { get; set; }
        /// <summary>
        /// The project name to publish to e.g: "Typemock Test"
        /// </summary>
        public InArgument<string> TeamProjectName { get; set; }
        /// <summary>
        /// The platform name to publish to e.g. Any CPU
        /// </summary>
        public InArgument<string> Platform { get; set; }
        /// <summary>
        /// The flavour (configuration) to publish to e.g. "Debug"
        /// </summary>
        public InArgument<string> Flavor { get; set; }
        /// <summary>
        /// Array of assembly names to test
        /// </summary>
        public InArgument<string[]> TestAssemblyNames { get; set; }
        /// <summary>
        /// Where to search for assemblies under test
        /// </summary>
        public InArgument<string> SearchPathRoot { get; set; }
        /// <summary>
        /// A single name result file
        /// </summary>
        public InArgument<string> ResultsFile { get; set; }
        /// <summary>
        /// A directory to store results in (tends not be used if the ResultFile is set)
        /// </summary>
        public InArgument<string> ResultsFileRoot { get; set; }
        /// <summary>
        /// The file that list as to how test should be run
        /// </summary>
        public InArgument<string> TestSettings { get; set; }
        // If your activity returns a value, derive from CodeActivity<TResult> 
        // and return the value from the Execute method. 
        protected override ExternalTestRunnerReturnCode Execute(CodeActivityContext context)
            String msTestOutput = string.Empty;
            ExternalTestRunnerReturnCode exitMessage = ExternalTestRunnerReturnCode.NotRun;
            if (CheckFileExists(TestRunnerExecutable.Get(context)) == false)
                LogError(context, string.Format("TestRunner not found {0}", TestRunnerExecutable.Get(context)));
                String mstest = MsTestExecutable.Get(context);
                if (CheckFileExists(mstest) == false)
                    mstest = GetDefaultMsTestPath();
                String testrunner = TestRunnerExecutable.Get(context);
                var arguments = new StringBuilder();
                arguments.Append(string.Format("\"{0}\"", mstest));
                arguments.Append(" /nologo ");
                // the files to test
                foreach (string name in TestAssemblyNames.Get(context))
                    arguments.Append(AddParameterIfNotNull("testcontainer", name));
                // settings about what to test
                arguments.Append(AddParameterIfNotNull("searchpathroot", SearchPathRoot.Get(context)));
                arguments.Append(AddParameterIfNotNull("testSettings", TestSettings.Get(context)));
                // now the publish bits
                if (string.IsNullOrEmpty(ProjectCollection.Get(context)) == false)
                    arguments.Append(AddParameterIfNotNull("publish", ProjectCollection.Get(context)));
                    arguments.Append(AddParameterIfNotNull("publishbuild", BuildNumber.Get(context)));
                    arguments.Append(AddParameterIfNotNull("teamproject", TeamProjectName.Get(context)));
                    arguments.Append(AddParameterIfNotNull("platform", Platform.Get(context)));
                    arguments.Append(AddParameterIfNotNull("flavor", Flavor.Get(context)));
                // where do the results go, tend to use one of these not both
                arguments.Append(AddParameterIfNotNull("resultsfile", ResultsFile.Get(context)));
                arguments.Append(AddParameterIfNotNull("resultsfileroot", ResultsFileRoot.Get(context)));
                LogMessage(context, string.Format("Call Mstest With Wrapper [{0}] and arguments [{1}]", testrunner, arguments.ToString()), BuildMessageImportance.Normal);
                using (System.Diagnostics.Process process = new System.Diagnostics.Process())
                    process.StartInfo.FileName = testrunner;
                    process.StartInfo.WorkingDirectory = SearchPathRoot.Get(context);
                    process.StartInfo.WindowStyle = ProcessWindowStyle.Normal;
                    process.StartInfo.UseShellExecute = false;
                    process.StartInfo.ErrorDialog = false;
                    process.StartInfo.CreateNoWindow = true;
                    process.StartInfo.RedirectStandardOutput = true;
                    process.StartInfo.Arguments = arguments.ToString();
                        msTestOutput = process.StandardOutput.ReadToEnd();
                        // for TypemockRunner and MSTest this is alway seems to be 1 so does not help tell if test passed or not
                        //  In general you can detect test failures by simply checking whether mstest.exe returned 0 or not.  
                        // I say in general because there is a known bug where on certain OSes mstest.exe sometimes returns 128 whether 
                        // successful or not, so mstest.exe 10.0 added a new command-line option /usestderr which causes it to write 
                        // something to standard error on failure.
                        // If (error data received)
                        //    FAIL
                        // Else If (exit code != 0 AND exit code != 128)
                        //    FAIL
                        // Else If (exit code == 128)
                        //    Write Warning about weird error code, but SUCCEED
                        // Else
                        //   SUCCEED
                        ///int exitCode = process.ExitCode;
                        LogMessage(context, string.Format("Output of ExternalTestRunner: {0}", msTestOutput), BuildMessageImportance.High);
                    catch (InvalidOperationException ex)
                        LogError(context, "ExternalTestRunner InvalidOperationException :" + ex.Message);
                    exitMessage = ParseResultsForSummary(msTestOutput);
            LogMessage(context, string.Format("ExternaTestRunner exiting with message [{0}]", exitMessage), BuildMessageImportance.High);
            return exitMessage;
        /// <summary>
        /// Adds a parameter to the MSTest line, it has been extracted to allow us to do a isEmpty chekc in one place
        /// </summary>
        /// <param name="parameterName">The name of the parameter</param>
        /// <param name="value">The string value</param>
        /// <returns>If the value is present a formated block is return</returns>
        private static string AddParameterIfNotNull(string parameterName, string value)
            var returnValue = string.Empty;
            if (string.IsNullOrEmpty(value) == false)
                returnValue = string.Format(" /{0}:\"{1}\"", parameterName, value);
            return returnValue;
        /// <summary>
        /// A handler to check the results for the success or failure message
        /// This is a rough way to do it, but is more reliable than the MSTest exit codes
        /// It returns a string as opposed to an  exit code so that it 
        /// Note this will not work of the /usestderr flag is used
        /// </summary>
        /// <param name="output">The output from the test run</param>
        /// <returns>A single line summary</returns>
        private static ExternalTestRunnerReturnCode ParseResultsForSummary(String output)
            ExternalTestRunnerReturnCode exitMessage = ExternalTestRunnerReturnCode.NotRun;
            if (Regex.IsMatch(output, "Test Run Failed"))
                exitMessage = ExternalTestRunnerReturnCode.Failed;
            else if (Regex.IsMatch(output, "Test Run Completed"))
                exitMessage = ExternalTestRunnerReturnCode.Passed;
                exitMessage = ExternalTestRunnerReturnCode.Unknown;
            return exitMessage;
        /// <summary>
        /// Handles finding MSTest, checking both the 32 and 64 bit paths
        /// </summary>
        /// <returns></returns>
        private static string GetDefaultMsTestPath()
            String mstest = @"C:\Program Files\Microsoft Visual Studio 10.0\Common7\IDE\mstest.exe";
            if (CheckFileExists(mstest) == false)
                mstest = @"C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\MSTest.exe";
                if (CheckFileExists(mstest) == false)
                    throw new System.IO.FileNotFoundException("MsTest file cannot be found");
            return mstest;
        /// <summary>
        /// Helper method so we log in both the VS Build and Debugger modes
        /// </summary>
        /// <param name="context">The workflow context</param>
        /// <param name="message">Our message</param>
        /// <param name="logLevel">Team build importance level</param>
        private static void LogMessage(CodeActivityContext context, string message, BuildMessageImportance logLevel)
            TrackingExtensions.TrackBuildMessage(context, message, logLevel);
        /// <summary>
        /// Helper method so we log in both the VS Build and Debugger modes
        /// </summary>
        /// <param name="context">The workflow context</param>
        /// <param name="message">Our message</param>
        private static void LogError(CodeActivityContext context, string message)
            TrackingExtensions.TrackBuildError(context, message);
        /// <summary>
        /// Helper to check a file name to make sure it not null and that the file it name is present
        /// </summary>
        /// <param name="fileName"></param>
        /// <returns></returns>
        private static bool CheckFileExists(string fileName)
            return !((string.IsNullOrEmpty(fileName) == true) || (File.Exists(fileName) == false));


This activity does need a good bit of configuring to use it in a real build. However, as said previously, the options it takes are basically those needed for the MSTest activity, so you just replace the existing calls to the MSTest activities as shown in the graph below should be enough.


Note: The version of the ExternalTestRunner activity in this post does not handle tests based on Metadata parameters (blue box above), but should be OK for all other usages (it is just that these parameters have not been wired through yet). The red box show the new activity in place (this is the path taken if the tests are controlled by a test setting file) and the green box contains an MSTest activity waiting to be swapped out (this is the path taken if no test setting or metadata files are provided).

The parameters on the activity in the red box are as follows, as said before they are basically the same as parameters for the standard MSTest activity.


The Result parameter (the Execute() method return value) does need to be associated with a variable declared in the workflow, in my case ExternalTestRunnerResult. This is defined at the sequence scope, the scope it is defined at must be such that it can be read by any other steps in the workflow that require the value. It is declared as being of the enum type ExternalTestRunnerReturnCode defined in the custom activity.


Further on in the workflow you need to edit the if statement that branches on whether the tests passed or not to use this ExtenalTestRunnerResult value


Once all this is done you should have all your MSTests running inside a Typemock’ed wrapper and all the results should be shown correctly in the build summary


And the log of the build should show you all the parameters that got passed through to the MSTest program.


Is there a better way to test a custom activity project?

Whilst sorting out the logic for the custom activity I did not want to have to go through the whole process of running the team build to test the activity, it just took too long. To speed this process I did the following

  1. In my solution I created a new Console Workflow project
  2. I referenced my custom activity project from this new workflow project
  3. I added my custom activity as the only item in my workflow
  4. For each parameter of the custom activity I created a matching argument for the workflow and wired the two together.

  5. I then created a Test Project that referenced the workflow project and custom activity project.
  6. In this I could write unit tests (well more integration tests really) that exercise many of the options in the custom activity. To help in this process I created some simple Test Projects assemblies that contained just passing tests, just failing test and a mixture of both.
  7. A sample test is shown below
    public void RunTestWithTwoNamedAssembly_OnePassingOneFailingTestsNoPublishNoMstestSpecified_FailMessage()
        // make sure we have no results file, MSTest fails if the file is present
        File.Delete(Directory.GetCurrentDirectory() + @"\TestResult.trx");
        var wf = new Workflow1();
        Dictionary<string, object> wfParams = new Dictionary<string, object>
            { "BuildNumber", string.Empty },
            { "Flavour", "Debug" },
            { "MsTestExecutable", string.Empty },
            { "Platform", "Any CPU" },
            { "ProjectCollection",string.Empty },
            { "TeamProjectName", string.Empty },
            { "TestAssemblyNames", new string[] { 
                Directory.GetCurrentDirectory() + @"\TestProjectWithPassingTest.dll",
                Directory.GetCurrentDirectory() + @"\TestProjectWithfailingTest.dll"
            { "TestRunnerExecutable", @"C:\Program Files (x86)\Typemock\Isolator\6.0\TMockRunner.exe" },
            { "ResultsFile", "TestResult.trx" }
        var results = WorkflowInvoker.Invoke(wf, wfParams);
        Assert.AreEqual(TypemockBuildActivity.ExternalTestRunnerReturnCode.Failed, results["ResultSummary"]);
  8. The only real limit here is that some of the options (the publishing ones) need a TFS server to be tested. You have to make a choice as to whether this type of publishing test is worth the effort of filling your local TFS server with test runs  from the test project or whether you want to test these features manually in a real build environment, especially give the issues I mention in my past post







So I have a working implementation of a custom activity that makes it easy to run Typemock based tests without losing any of the other features of a Team Build. Butt as I learnt getting around the deployment issues can be a real pain.

The Teamprise Eclipse plug in for TFS gets a new name

As I am sure you remember a few months ago Microsoft bought Teamprise and their Java clients for TFS. Well the team has got out their first Microsoft branded release, details can be found on Martin Woodward’s and Brian Harry’s blogs. This beta provides the first support for TFS2010

This release is very timely as I will be talking on the Java integration via the Eclipse plug-in at QCON next week and at the Architect Insight Conference at the end of the month. This  “Eaglestone” release means I can hopefully do my demos against TFS2010.

The importance of using parameters in vs2010 build workflows

I have been doing some more work integrating Typemock and VS 2010 Team Build. I have just wasted a good few hours wondering why my test results are not being published.

If I looked at the build log I saw my tests ran (and pass or failed as expected) and then were published without error.


But when I checked the build summary it said there were no tests associated with the build, it reporting “No Test Results”


This was strange it had been working in the past. After much fiddling around I found the problem, it was twofold:

  • The main problems was that in my InvokeMethod call to run Typemock/MSTest I had hard coded the Platform: and Flavor: values. This meant irrespective of the build I asked for, I published my test results to the Any CPU|Debug configurations. MSTest lets you do this, even if no build of that configuration exists at the time.

My InvokeMethod argument parameter should be been something like

"""C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\MSTest.exe""  /nologo /testcontainer:""" + String.Format("{0}\Binaries\BusinessLogic.Tests.dll", BuildDirectory) + """ /publish:""http://typhoon:8080/tfs/DefaultCollection"" /publishbuild:""" + BuildDetail.Uri.ToString() + """ /teamproject:""" + BuildDetail.TeamProject + """ /platform:""" + platformConfiguration.Platform + """ /flavor:""" + platformConfiguration.Configuration + """ /resultsfile:""" + String.Format("{0}\Binaries\Test.Trx", BuildDirectory) + """  "

  • The second issues was that I had failed, on at least one of my test build definitions, to set the Configurations to Build setting. This meant the build defaulted to Mixed Platforms|Debug (hence not matching my hard coded Any CPU|Debug configuration). Interesting to note here if that the parameters used above (platformConfiguration.Configuration and platformConfiguration.Platform ) are both empty if the Configurations to Build setting is not set. MSBuild is the activity that chooses the defaults not the workflow. So in effect you must always set these values for your build, or you will need to handle these empty strings in the workflow if you don’t want MSTest to fail saying the Platforms and Flavour parameters are empty. Seem to me explicitly setting them is good practice anyway.


So the technical tip here is make sure that you correctly us all the parameters associated with a workflow in activities. You cannot trust an activity to give and error or warning if you pass it strange values.

Do you use a Symbol Server?

I find one of the most often overlooked new features of 2010 is the Symbol Server. This is a file share where the .PDB symbol files are stored for any given build (generated by a build server, see Jim Lamb’s post on the setup). If you look on the symbol server share you will see directories for each built assembly with a GUID named subdirectory containing the PDB files for each unique build.

So what is this Symbol Server used for? Well you can use the Symbol Server to enable debug features such as Intellitrace, vital if you are using Lab Manager. In effect this means that when viewing an Intellitrace log Visual Studio is able to go to the Symbol Server to get the correct .PDB file for the assemblies being run, even if the source is not available, thus allowing you to step through the code. It can also be used for remote debugging of ASP.NET servers.

A bonus is that you can debug release code, as long as you produced .PDB symbols and placed them on the Symbol Server when you built the release (by altering the advanced build properties shown below).


Key to remember here is that the client PC that generates the Intellitrace file does not need access to the PDB files, only the PC handling the debugging process needs to be able to access the symbols. Perfect for release codes scenarios.

This ability to debug into code that you don’t have the source for extends to debugging into Microsoft .NET framework code. Microsoft have made public a Symbol Server for just this purpose. To use it you have to enable it using the Tool > Option > Debugging > Symbols dialog.


All this should make debugging that hard to track problem just that bit easier.

Speaking at QCon on TFS and Java Integration

Week after next I will be speaking at QCon London with Simon Thurman of Microsoft on “The Interoperable Platform”.

So what does that title mean? Well for me, for this session, it will be about how you can use the ALM features of TFS even when using Eclipse for Java development. So it will be a demo led session on the Teamprise tools for Eclipse and how they can allow you to build a unified development team that works in both .NET and Java.

Should be an interesting event, the list of speaker looks great. Shame I will only be there for a day