单独的AppDomain

时间:2016-05-18 15:32:08

标签: reflection msbuild appdomain msbuild-task

结束目标:

我希望有一个自定义构建任务,它接受我编译的程序集,并提取特定属性的所有实例以进行自动文档和卸载。 (在这种情况下,一组COM可见类型的GUID属性)。

问题:

在阅读了一些例子后,使用Inline Build Task的前景相当诱人。但是,我的任务需要反映构建的程序集并从中提取某些元数据(特别是属性)。

在程序集上反映的catch会锁定输出文件,直到卸载AppDomain,在这种情况下,只有在Visual Studio关闭时才会出现这种情况。结果:每次会话只能构建一次。

我看到存在特殊的构建任务类,即AppDomainIsolatedTask,但我找不到任何示例或证据表明此类可用于内联任务。

问题:

是否可以在单独的AppDomain中运行内联构建任务?如果是这样,那怎么样?

代码示例:(尽可能短)

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

这些的构建输出是:

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

2 个答案:

答案 0 :(得分:1)

不,这是不可能的。

内联任务使用CodeTaskFactory创建任务,如果您take a look at the code on GitHub,您将看到在编译包含代码的程序集后,任务的实例正在使用{{1创建}}。这意味着它始终与MSBuild在同一AppDomain中创建。

将其与pre-compiled tasks进行比较,后者使用TaskLoader类创建任务实例,Activator.CreateInstance在任务类型上查找TaskLoader并创建如果找到了单独的AppDomain中的实例。

最简单的解决方案是将内联代码转换为预编译任务。这很容易做到:

  1. 创建一个类库项目
  2. 参考LoadInSeparateAppDomainAttribute
  3. 为您的任务创建一个类。
  4. 编译项​​目。
  5. Microsoft.Build.Utilities元素替换为指定包含任务的程序集的元素。
  6. 以下是一个例子:

    包含任务的类库:

    UsingTask

    您的MSBuild文件:

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

    输出:

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

答案 1 :(得分:0)

或者,您可以使用Exec命令在单独的MsBuild.exe进程中运行作业,并传入相同的当前目标文件和所需的属性。

<Project ToolsVersion="15.0" DefaultTargets="RunCodeFromTargetPath" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <RunCodeFromTargetPathAfterTargets>PrepareForRun</RunCodeFromTargetPathAfterTargets>
  </PropertyGroup>
  <Target Name="RunCodeFromTargetPath" AfterTargets="$(RunCodeFromTargetPathAfterTargets)">
    <PropertyGroup Condition="'$(PlatformTarget)' == 'x64'">
      <MsBuildFolderLocation >\amd64</MsBuildFolderLocation><!--support for x64 only assemblies -->
    </PropertyGroup>
    <Exec Command="&quot;$(MSBuildBinPath)$(MsBuildFolderLocation)\MSBuild.exe&quot; /target:RunCodeFromTargetPathInternal /p:TargetPath=&quot;$(TargetPath.Replace('\','\\'))&quot; &quot;$(MSBuildThisFileFullPath)&quot;" />
  </Target>
  <Target Name="RunCodeFromTargetPathInternal">
    <RunCodeFromTargetPathTask />
  </Target>
  <UsingTask
    TaskName="RunCodeFromTargetPathTask"
    TaskFactory="CodeTaskFactory"
    AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll">
    <Task>
      <Reference Include="$(TargetPath)" />
      <Code Type="Fragment" Language="cs">
        <![CDATA[
          // your code here
        ]]>
      </Code>
    </Task>
  </UsingTask>
</Project>