Вот простой способ выполнять процессы асинхронно, используя только msbuild и встроенные задачи. Это только для MSBuild V4.0 и выше (слава Богу, ребята из MSBuild за добавление этой функции!). Вам не нужны никакие внешние пакеты расширений.
По сути, мы берем код, предложенный выше, и помещаем его в встроенную задачу. Не стесняйтесь поиграть с кодом, отвечающим вашим потребностям.
Суть этого решения в том, что оно позволяет достичь результата без головной боли при создании отдельной библиотеки DLL для пользовательской задачи . Реализация в пакете расширений определенно более надежна, но это работает как быстрый и грязный способ решения этой проблемы. Вы также можете настроить, как именно вы хотите, чтобы он работал.
<!--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>
Затем вы можете вызвать задачу ExecAsync из вашего обычного сценария следующим образом. Примечание. Мой сценарий ниже используется для сбора покрытия кода для приложения.
<!--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."/>
Вот описание того, что там происходит:
- Сначала я запускаю инструмент производительности, чтобы он слушал
охват. Я делаю это, используя нашу задачу AsyncExec, потому что обычно
блоки инструментов при работе в MSBuild (см. здесь ).
- Далее мы запускаем нашу программу, о которой мы хотим получить покрытие.
- Затем мы закрываем инструмент покрытия, когда закончим.