Development Blog

 Thursday, March 01, 2007
« Loosely-coupled Mocks | Main | MVP Summit and MonoRail Podcast »

I needed the ability to execute a task from an MsBuild project and have MsBuild keep going and not wait around on the process to exit. I was hoping to find an option on the Exec task for this but that search turned up nothing. After scowering a bit I couldn't find one somebody else wrote, so here we go:

using System;
using System.Collections;
using System.Collections.Specialized;
using System.Diagnostics;

using Microsoft.Build.Tasks;

namespace AsyncExec
{
  public class AsyncExec : Exec
  {
    #region Task Members
    protected override int ExecuteTool(string pathToTool, string responseFileCommands, string commandLineCommands)
    {
      Process process = new Process();
      process.StartInfo = GetProcessStartInfo(pathToTool, commandLineCommands);
      process.Start();
      return 0;
    }
    #endregion

    #region Methods
    protected virtual ProcessStartInfo GetProcessStartInfo(string executable, string arguments)
    {
      if (arguments.Length > 0x7d00)
      {
        this.Log.LogWarningWithCodeFromResources("ToolTask.CommandTooLong", new object[] { base.GetType().Name });
      }
      ProcessStartInfo startInfo = new ProcessStartInfo(executable, arguments);
      startInfo.WindowStyle = ProcessWindowStyle.Hidden;
      startInfo.CreateNoWindow = true;
      startInfo.UseShellExecute = true;
      string workingDirectory = this.GetWorkingDirectory();
      if (workingDirectory != null)
      {
        startInfo.WorkingDirectory = workingDirectory;
      }
      StringDictionary environmentOverride = this.EnvironmentOverride;
      if (environmentOverride != null)
      {
        foreach (DictionaryEntry entry in environmentOverride)
        {
          startInfo.EnvironmentVariables.Remove(entry.Key.ToString());
          startInfo.EnvironmentVariables.Add(entry.Key.ToString(), entry.Value.ToString());
        }
      }
      return startInfo;
    }
    #endregion
  }
}

Don't fret over the WindowStyle and CreateNoWindow flags, Exec works by spawning cmd.exe with a batch file that it generates with our command. Without those flags, the command prompt window flashes by.

Obviously, very rough stuff and I make no promises of it's abilities. It worked for the simple example below and that's all I wanted. Ideally, the task would ensure the executable being started exists so we could at least fail if that's the case. But, for now this works for us. It's only used in interactive situations to spawn NCoverExplorer and things like thing. We can already easily spawn the HTML reports by using:

<Exec Command="$(ArtifactsDirectory)\mbunit-tests.html" />

But that just doesn't work when you need to run an application and not care about when it's closed:

<AsyncExec Command="$(ToolsDirectory)\NCoverExplorer\NCoverExplorer.exe $(ArtifactsDirectory)\coverage.xml" />

So here's the binaries in case somebody else is curious as well as the UsingTask.

<UsingTask AssemblyFile="AsyncExec.dll" TaskName="AsyncExec.AsyncExec" />

Obviously, if something like this is already easily possible and this was a half hour wasted, let me know. Especially if it's possible "out-of-the-box". I'm always glad to have one less dependency.

Source and Binaries

source | tools | msbuild