MSBuild exec task without blocking

msbuild

Would anyone happen to know a trick that will keep this MSBuild task from blocking? I really just want the explorer to open and the build script to keep on going. Currently it blocks at the Exec task until the explorer window is closed.

<Target Name="OpenExplorer">
    <Exec Command='explorer.exe "$(DestinationDir)"' IgnoreExitCode="true" />
</Target>

Thanks!

Edit: I was hoping to avoid creating a custom task for this. Perhaps some command line magic exists that could be placed inline for the Command?

Best Answer

Here is an easy way to execute processes asynchronously by only using msbuild and inline tasks. This is only for MSBuild V4.0 and up (God bless the MSBuild guys for adding this feature!). You don't need the any external extension packs.

Effectively, we are taking the code suggested above and putting it in an inline task. Feel free to fiddle with the code to suite your needs.

The point of this solution is that it lets you achieve the result without the headache of creating a separate dll for the custom task. The implementation in the extension pack is definitely more solid but this works as a quick 'n dirty way of solving this issue. You can also customise exactly how you want it to run.

  <!--Launch a Process in Parallel-->
  <UsingTask TaskName="ExecAsync" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
    <ParameterGroup>
      <!--The file path is the full path to the executable file to run-->
      <FilePath ParameterType="System.String" Required="true" />
      <!--The arguments should contain all the command line arguments that need to be sent to the application-->
      <Arguments ParameterType="System.String" Required="true" />
    </ParameterGroup>
    <Task>
      <Code Type="Fragment" Language="cs">
        <![CDATA[
  string name = System.IO.Path.GetFileNameWithoutExtension(FilePath);
  Log.LogMessage("Starting {0}...", name);        
  System.Diagnostics.ProcessStartInfo processStartInfo = new System.Diagnostics.ProcessStartInfo(FilePath, Arguments);
  processStartInfo.UseShellExecute = true;
  System.Diagnostics.Process.Start(processStartInfo);
  Log.LogMessage("Finished running process {0}.", name);
  ]]>
      </Code>
    </Task>
  </UsingTask>

You can then call the ExecAsync task from within your normal script in the following fashion. Note: My script below is used to gather code coverage for an application.

<!--Start listening for coverage data:-->
<Message Text="Starting to listen for coverage..."/>
<ExecAsync FilePath='$(VSPerfCmdExePath)' Arguments='/start:coverage /output:"$(CoverageFilePath)"' ContinueOnError='true'/>
<Message Text="Listening for coverage..."/>

<!--Start App with Coverage:-->
<Message Text="Starting App..."/>
<Exec Command='"$(AppCoverageLatestExePath)"' ContinueOnError='true' WorkingDirectory='$(AppCoverageLatestFolder)'/>
<Message Text="App shut down."/>

<!--Stop gathering coverage results:-->
<Message Text="Stopping listening for coverage..."/>
<Exec Command='"$(VSPerfCmdExePath)" /shutdown'/>
<Message Text="Coverage shut down."/>

Here is a description of what is happening there:

  1. First I kick the performance tool off so that it listens for coverage. I do this using our AsyncExec task because normally the tool blocks when running in MSBuild (see here).
  2. Next, we start our program that we want to gather coverage on.
  3. Then we shut down the coverage tool once we are done.
Related Topic