【问题标题】:Custom MSBuild Task locks assembly自定义 MSBuild 任务锁定程序集
【发布时间】:2011-05-26 12:48:05
【问题描述】:

乍一看,我的问题似乎很常见:我的一些项目中有一个自定义的 MS Build 任务。编译项目后,我无法再编译构建任务 - 构建任务程序集被 Visual Studio 锁定。

我在这里发现了很多帖子说“只是从 AppDomainIsolatedTask 继承”。

我的任务已经完成了。除了这个任务,程序集不包含任何其他内容。 AppDomain 似乎已卸载,至少触发了 DomainUnload 事件。并且,依赖程序集被正确卸载。

但是,包含构建任务本身的程序集被 devenv.exe 锁定(我通过 ProcessExplorer 进行了双重检查)。

我发现另一个帖子说“将 GenerateResourceNeverLockTypeAssemblies 属性设置为 true”,这听起来很有希望,但也没有帮助。

所以,我想知道还有什么问题。无论我使用 VS2008 还是 2010,行为都是一样的。

【问题讨论】:

  • 这不是同一个问题。我遇到了同样的问题,问题不在于任务程序集被锁定(这是可以理解的)。问题是您在任务中加载的任何 dll 文件(例如 Assembly.LoadFrom())即使在您的任务的 AppDomain 被释放后也永远不会被释放,即使您自己创建另一个 AppDomain 并从那里加载 dll:关闭 AppDomain 后,dll 仍处于锁定状态。相同的代码在控制台应用程序上工作正常(程序仍在运行,但如果你关闭 appdomain,dll 将被释放),但在 VS 任务上它的行为不同
  • 这不是您问题的答案,但如果您在 VS IDE 中执行某种代码生成,而 IDE 正在运行,那么您最好利用 T4 模板或 VS 自定义工具。这应该巧妙地避开您的问题,特别是如果您使用 T4 模板。
  • 你有没有找到解决这个问题的方法?我看到了类似的东西。

标签: msbuild msbuild-task


【解决方案1】:

这种事情可能非常棘手。有很多需要考虑的 LoaderOptimization、ShadowCopy 等。

AppDomainIsolatedTask 并不意味着“不要污染构建域”。 这意味着“在另一个域中运行我的代码”——任务类型本身仍被加载到构建引擎域中。这很不幸,因为我觉得大多数人将其解释为前者。要使这种专门的任务类型起作用,您还必须使用[LoadInSeparateAppDomain] 来装饰您的任务。这仍然不能解决您的问题。

ProcessExplorer 有这个方便的 .NET 程序集视图,您可以使用它来识别哪个域拥有程序集的句柄。如您所知,devenv.exe 已将其锁定。

由于您将具有任务的 DLL 加载到 VS 用于构建内容的域中,您将遇到此问题。

你需要做的是引入另一个间接层。

选项

  1. 您可以将任务转换为inline code task
  2. 您可以使用内联代码任务来加载您的静态任务并委托给它。

我为方法 #2 提供了一个示例 此屏幕截图验证了我的自定义库不存在于 MSBuild 应用程序域中的结果。

像这样定义一个内联任务

<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">  
  <UsingTask  
    TaskName="RunOtherTask"  
    TaskFactory="CodeTaskFactory"  AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll" >  
    <ParameterGroup />  
    <Task>
      <Using Namespace="MyTasks" />  
      <Code Type="Class" Language="cs">  
<![CDATA[ 
using System;  
using System.Reflection;
using Microsoft.Build.Framework;  
using Microsoft.Build.Utilities;  

namespace MyTasks {      
    public class RunOtherTask : Task {  
        public override bool Execute() { 
           var remoteTask = CreateInstanceInSeparateDomain();      
           remoteTask.BuildEngine = this.BuildEngine;
           remoteTask.Execute();

           return true;  
        }

        private ITask CreateInstanceInSeparateDomain() {
            var thisAssembly = Assembly.GetExecutingAssembly();

            var setup = new AppDomainSetup {
                ShadowCopyFiles = true.ToString(),              
                LoaderOptimization = LoaderOptimization.MultiDomainHost                 
            };

            var domain = AppDomain.CreateDomain("MyDomain", null, setup);
            var handle = domain.CreateInstanceFrom(@"C:\ClassLibrary1.dll", "ClassLibrary1.SimpleTask");

            return (ITask)handle.Unwrap();
        }
    }
} 
 ]]>
      </Code>  
    </Task>  
  </UsingTask>  

  <Target Name="RunTask">  
    <RunOtherTask /> 
  </Target>

</Project>  

只要您的任务实现了 ITask(它必须),它就可以工作。 您需要在新实例化的任务上设置引擎属性,以使一切正常工作。

【讨论】:

  • 这种笨拙的解决方法应该没有必要。似乎根本问题是构建完成时没有正确丢弃 MSBuild AppDomain。 (或者丢弃它并不能正确清理它的进程和锁。)
【解决方案2】:

可以通过更改文件系统权限安全来解决。将您当前的用户设置为锁定文件的共同所有者。

【讨论】:

    猜你喜欢
    • 2011-03-23
    • 1970-01-01
    • 1970-01-01
    • 2021-09-17
    • 1970-01-01
    • 1970-01-01
    • 2012-10-29
    • 2012-03-16
    • 2011-11-27
    相关资源
    最近更新 更多