Visual Studio 2008锁定自定义MSBuild任务程序集

28

我正在开发一个自定义的 MSBuild 任务,用于构建一个 ORM 层 并在项目中使用。但是,我受到了 Visual Studio 的影响,它会持有 MSBuild 任务 DLL,并且不会释放。

我希望按照以下方式组织我的解决方案;

My Solution
 |
 +- (1) ORM Layer Custom Task Project
 |  |
 |  +- BuildOrmLayerTask.cs     // here's my task
 |  
 +- (2) Business Logic Project  // and here's the project that uses it.
    |
    +- <UsingTask TaskName="BuildOrmLayerTask" AssemblyFile="$(TaskAssembly)" />

然而,当项目(2)构建时,它会锁定来自项目(1)的程序集。因此,现在我无法在不关闭解决方案并重新打开它的情况下再次构建项目(1)。
有没有办法组织事情,使得自定义构建任务不被Visual Studio锁定?
3个回答

26

(编辑:Sayed Ibrahim Hashimi,他是MSBuild的权威,建议使用AppDomainIsolatedTask类来实现更好的方法)

我已经自己解决了这个问题...

找到了Microsoft的MSBuild开发人员之一Dan Moseley的论坛帖子:

你好,

不幸的是,这是因为MSBuild在主应用程序域中加载任务程序集。CLR不允许从应用程序域卸载程序集,因为这会对它们进行重要的优化。

我建议的唯一解决方法是调用msbuild.exe来构建使用该任务的项目。为此,请将MSBuild.exe<>创建为VS中的外部工具。

Dan
msbuild开发人员
DanMoseley - MSFT

因此,为了停止锁定,必须生成一个新的MSBuild.exe进程。它不能是在Visual Studio内运行的那个,因为当MSBuild运行时,它会将任务加载到Visual Studio的主应用程序域中,这是永远无法卸载的。

  • create a new MSBuild project (a .csproj or similar) which overrides the 'Build' Target and performs your custom actions, eg;

    <!-- fragment of Prebuild.csproj -->   
    <Target Name="Build">   
         <BuildOrmLayerTask Repository="$(Repository)" />   
    </Target>
    
  • Add it to visual studio if you want, but use Configuration Manager to make sure it is notbuilt in any configuration. Just let VS take care of source control and suchlike, not building.

  • Edit the .csproj file of the project that depends on Prebuild.csproj. Add a BeforeBuild target which invokes MSBuild using the Exec task. This will start a new process, and when that process ends, the file locks are released. Example;

    <PropertyGroup>   
         <PrebuildProject>$(SolutionDir)Prebuild\Prebuild.csproj</PrebuildProject>   
    </PropertyGroup>   
    <Target Name="BeforeBuild">   
         <Exec Command="msbuild.exe &quot;$(PrebuildProject)&quot;" />   
    </Target>
    
现在,当您构建依赖项目时,在运行编译之前,它会在新进程中执行MSBuild。

4
启动另一个msbuild.exe进程似乎是唯一有效的解决方案。在这种情况下,我不认为扩展AppDomainIsolatedTask会起作用,因为MSBuild已经在尝试读取类型时在主AppDomain中加载了该程序集。需要在项目文件本身中提供一种方法告诉MSBuild在另一个AppDomain中加载任务。微软拒绝修复这个重大问题,因为使用新的AppDomains会导致性能损失,但是让我们覆盖选项并自行决定如何处理?为什么这对MS“工程师”来说如此难以解决呢? - makhdumi
1
扩展AppDomainIsolatedTask对我没有起作用。 - Fosna
2
您不必创建单独的MSBuild项目。您可以在目标上简单地设置条件,并使“BeforeBuild”目标使用一些参数执行当前项目,例如<Target Name="TypeLite" Condition="$(ExecuteTypeLite)"><Exec Command="&quot;$(MSBuildBinPath)\MSBuild.exe&quot; /t:TypeLite /p:ExecuteTypeLite=true &quot;$(ProjectDir)$(ProjectFileName)&quot;" /> - user247702

4
你能否编辑项目文件并包含以下属性声明。
<PropertyGroup>
    <GenerateResourceNeverLockTypeAssemblies>true</GenerateResourceNeverLockTypeAssemblies>
</PropertyGroup>

如果这个对你有用,请告诉我。

我把这个加到了我的所有项目中,现在完美运行。谢谢! - Steve Cooper
实际上,当我开始使用它时,它又卡住了。不过我已经找到解决办法了,请看我的答案。似乎VS将自定义任务加载到主要应用程序域中,而且永远无法卸载,因此您只能使用Exec启动新的MSBuild.exe进程。 - Steve Cooper
8
如果这是一个定制任务,你可以扩展 Microsoft.Build.Utilities.AppDomainIsolatedTask 来帮助你完成。 - Sayed Ibrahim Hashimi
我尝试了这个,重新启动了我的Visual Studio,它一直工作到我选择“部署”。如果您部署一个.dll文件,MSBuild会锁定自定义任务。 - Raffaeu
3
请问您需要翻译的内容是:@SayedIbrahimHashimi,您能否解释一下在这种情况下扩展AppDomainIsolatedTask应该如何工作?我可以看到当您加载自定义任务中的程序集被锁定时,它将如何工作,但是对于MSBuild本身加载的程序集,这将如何运作呢?难道MSBuild不需要在看到其中有AppDomainIsolatedTasks之前先加载程序集吗?为什么UsingTask元素没有提供类似的选项? - makhdumi
@Al-Muhandis 从深夜通过 ildasm 在不同的构建引擎 DLLs 中进行搜查,我必须得说你是非常正确的。装配程序加载实际上使用的是 Assembly.LoadAssembly.LoadFrom,具体取决于您在 UsingTask 中是指定了 AssemblyName 还是 AssemblyFile。任何继承自 MarshalByRefObject 的任务似乎都会在运行之前被加载到新创建的 AppDomain 中,但是发现过程是通过将装配程序加载到默认 app domain 中完成的。我正在探索编写一个包装器任务来帮助,但今晚不做了。 - Andrew

3

正如我在针对@Al-Muhandis的评论中提到的,似乎可以创建一个包装器来包装自定义任务,以便包装器被锁定但不锁定自定义任务DLL。 我已经开始使用isolated-task项目尝试这样做。 它可能存在错误,并且目前仅适用于VS2008。 欢迎提交拉取请求。

该项目的想法基于这样一个观察结果:从MarshalByRefObject派生的任务(使用AppDomainIsolatedTask)似乎会为了反射目的而加载到主应用程序域中,但是为了执行任务而创建了一个新的应用程序域。 由于加载到主应用程序域仍然会锁定DLL,因此有必要创建一个具有从AppDomainIsolatedTask派生的任务的DLL,以加载自定义任务DLL。 这样,包装器DLL将被锁定,但由于它在自己的应用程序域中执行,因此当卸载包装器任务的执行域时,自定义任务DLL将被卸载。 这个过程避免了在构建完成后保持自定义任务DLL锁定的情况。


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