2016-05-18 22 views
1

End Ziel:Inline MS Aufgabe beim Aufbau in separaten AppDomain

Ich möchte eine benutzerdefinierte Build-Aufgabe haben, die meine kompilierten Assembly nimmt, und extrahiert alle Instanzen eines bestimmten Attribut für die automatisierte Dokumentation und Deinstallation. (In diesem Fall das GUID-Attribut für eine Reihe von COM-sichtbaren Typen).

Problem:

Nach ein paar Beispiele zu lesen, die Aussicht auf eine Inline Build Task der Verwendung war ziemlich verlockend. Allerdings muss meine Aufgabe über die gebauten Assemblies hinweg reflektiert werden und bestimmte Metadaten daraus extrahieren (speziell Attribute).

Der Catch widerspiegelt die Assembly wird die Ausgabedatei sperren, bis die AppDomain entladen wird, die in diesem Fall nur zu sein scheint, wenn Visual Studio geschlossen ist. Das Ergebnis: Der Build kann nur einmal pro Sitzung erstellt werden.

Ich sehe, dass es spezielle Build-Aufgabenklassen gibt, nämlich AppDomainIsolatedTask, aber ich kann keine Beispiele oder Beweise finden, dass diese Klasse für eine Inline-Aufgabe verwendet werden kann.

Frage:

Ist es möglich, eine Inline-Build-Aufgabe in einem separaten AppDomain zu laufen? Wenn ja, wie?

Codebeispiel: (so kurz wie möglich)

<UsingTask TaskName="InDomainTask" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll"> 
<Task><Code Type="Class" Language="cs"><![CDATA[ 

    public class InDomainTask : Microsoft.Build.Utilities.Task 
    { 
     public override bool Execute() 
     { 
      Log.LogMessage("InDomainTask AppDomain.Id = " + System.AppDomain.CurrentDomain.Id); 
      return true; 
     } 
    } 

]]></Code></Task> 
</UsingTask> 
<UsingTask TaskName="OutDomainTask" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll"> 
<Task><Code Type="Class" Language="cs"><![CDATA[ 

    [Microsoft.Build.Framework.LoadInSeparateAppDomain] 
    public class OutDomainTask : Microsoft.Build.Utilities.AppDomainIsolatedTask 
    { 
     public override bool Execute() 
     { 
      Log.LogMessage("OutDomainTask AppDomain.Id = " + System.AppDomain.CurrentDomain.Id); 
      return true; 
     } 
    } 

]]></Code></Task> 
</UsingTask> 

<Target Name="AfterBuild" AfterTargets="Compile"> 
    <InDomainTask /> 
    <OutDomainTask /> 
</Target> 

Die Build-Ausgabe davon ist:

 
1> InDomainTask AppDomain.Id = 1 
1> OutDomainTask AppDomain.Id = 1 

Antwort

0

Nein, es ist nicht möglich.

verwenden Inline Aufgaben der CodeTaskFactory die Aufgabe zu erstellen, und wenn Sie take a look at the code on GitHub, werden Sie sehen, dass nach einer Baugruppe zusammenzustellen, die den Code enthält, wird eine Instanz der Aufgabe mit Activator.CreateInstance zu schaffen. Dies bedeutet, dass es immer in der gleichen Anwendungsdomäne wie MSBuild erstellt wird.

Vergleichen Sie das mit pre-compiled tasks, die die TaskLoader-Klasse verwenden, um eine Instanz der Aufgabe zu erstellen, und dass TaskLoader für die LoadInSeparateAppDomainAttribute auf dem Aufgabentyp suchen und erstellt eine Instanz in einem separaten AppDomain, wenn es gefunden.

Die einfachste Lösung besteht darin, Ihren Inline-Code in eine vorkompilierte Aufgabe umzuwandeln. Es ist wirklich einfach zu tun:

  1. eine Klassenbibliothek Projekt erstellen
  2. Referenz Microsoft.Build.Utilities.
  3. Erstellen Sie eine Klasse für Ihre Aufgabe.
  4. Kompilieren Sie das Projekt.
  5. Ersetzen Sie Ihr UsingTask Element durch eines, das die Assembly angibt, die die Aufgaben enthält.

Hier ist ein Beispiel:

Ihre Klassenbibliothek, die die Aufgaben enthält:

public class InDomainTask : Microsoft.Build.Utilities.Task 
{ 
    public override bool Execute() 
    { 
     Log.LogMessage(System.AppDomain.CurrentDomain.FriendlyName); 
     return true; 
    } 
} 

public class OutDomainTask : Microsoft.Build.Utilities.AppDomainIsolatedTask 
{ 
    public override bool Execute() 
    { 
     Log.LogMessage(System.AppDomain.CurrentDomain.FriendlyName); 
     return true; 
    } 
} 

Ihre MSBuild-Datei:

<UsingTask TaskName="InDomainTask" AssemblyFile="path\to\the\class\library.dll"/> 
<UsingTask TaskName="OutDomainTask" AssemblyFile="path\to\the\class\library.dll"/> 

<Target Name="AfterBuild" AfterTargets="Compile"> 
    <InDomainTask/> 
    <OutDomainTask/> 
</Target> 

Diese Ausgänge:

MSBuild.exe 
taskAppDomain (in-proc)