自定义MSBuild任务锁定程序集

18

乍一看,我的问题似乎是一个普遍存在的问题:我在一些项目中有一个自定义的MS Build任务。一旦我编译了这些项目,我就不能再编译构建任务了——构建任务程序集被Visual Studio锁定。

我在这里找到了很多帖子都说“只需从AppDomainIsolatedTask继承即可”。

我的任务已经这么做了。程序集中除了这个任务什么也没有。AppDomain似乎已经被卸载了,至少已触发了DomainUnload事件。而且,相关程序集已正确卸载。

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

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

所以,我想知道还可能出现什么问题。无论我使用VS2008还是2010,行为都是相同的。


检查 https://dev59.com/5nA75IYBdhLWcg3wS3BA 的答案。 - alexandrul
1
这不是同一个问题。我遇到了同样的问题,但问题并不是任务程序集被锁定(这是可以理解的)。问题在于,您在任务中加载的任何dll文件(例如Assembly.LoadFrom())即使在任务的AppDomain被处理后也永远不会被释放,即使您自己创建另一个AppDomain并从那里加载dll:在关闭AppDomain后,dll仍然被锁定。相同的代码在控制台应用程序上运行正常(程序仍在运行,但是如果您关闭appdomain,则dll将被释放),但在VS任务中表现不同。 - Sheepy
这不是对你问题的回答,但如果你正在使用VS IDE进行某种代码生成,并且IDE正在运行,则最好利用T4模板或VS自定义工具。这应该可以很好地解决你的问题,特别是如果你使用T4模板。 - Umar Farooq Khawaja
你曾经找到解决这个问题的方法吗?我也遇到了类似的情况。 - Darrell
2个回答

4
这种事情很棘手,很多方面需要考虑,包括LoaderOptimization、ShadowCopy等。 AppDomainIsolatedTask并不意味着“不要污染构建域”,而是意味着“在另一个域中运行我的代码”——任务类型本身仍然加载到构建引擎域中。这很不幸,因为我认为大多数人会将其解释为前者。为了使这种专业的任务类型起作用,您还必须使用[LoadInSeparateAppDomain]来装饰您的任务。但这仍然无法解决您的问题。
ProcessExplorer有这个方便的.NET程序集视图,您可以使用它来确定哪个域具有对您的程序集的句柄。就像您已经知道的那样,devenv.exe已经将其锁定。

Useful feature

由于您加载了包含任务的DLL到VS用于构建的域中,因此会遇到这个问题。

您需要引入另一层间接性。

选项

  1. 您可以将自己的任务转换为内联代码任务
  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接口(必须这样),它就会起作用。您需要为新实例化的任务设置引擎属性,以使一切正常运行。

Class Library1 is not loaded


这种笨拙的解决方法不应该是必需的。似乎根本问题在于 MSBuild AppDomain 在构建完成后没有被正确地丢弃。 (或丢弃它时没有正确清理其进程和锁定。) - StackOverthrow

-5

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接