Задача MSBuild exec без блокировки

Кто-нибудь случайно узнает трюк, который заставит эту задачу MSBuild блокироваться? Я просто хочу, чтобы проводник был открыт, а сборка script продолжалась. В настоящее время он блокируется в задаче Exec до закрытия окна проводника.

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

Спасибо!

Изменить: Я надеялся избежать создания настраиваемой задачи для этого. Возможно, существует какая-то маска командной строки, которая может быть встроена в строку Command?

Ответ 1

Вы не можете сделать это с помощью native Exec. Но вы можете написать свой собственный, который срабатывает асинхронно, как в в этом примере:

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

    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;
    }
  }

который вы можете запустить с помощью:

<AsyncExec Command="..." />

Ответ 2

Вот простой способ выполнения процессов асинхронно, используя только команды 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 из вашего обычного script следующим образом. Примечание. Мой script ниже используется для сбора покрытия кода для приложения.

<!--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 (см. здесь).
  • Затем мы начинаем нашу программу, которую хотим получить на сайте.
  • Затем мы завершим инструмент покрытия, как только мы закончим.

Ответ 5

Команда в Exec помещается в пакетный файл и выполняется. Таким образом, вы можете использовать ключевое слово "start" в команде так же, как в окне консоли. Это сделает трюк.