在msbuild中反射出现问题,但devenv没有问题。

3

我查看了与此相关的SO问题,但没有找到解决这个问题的任何信息,因此我提出了这个问题。

我有一个构建任务(添加为AfterBuild目标),用于验证类型名称。这些类型名称是正在构建的silverlight项目中的完全限定类型名称。

为了解析这些类型名称,我使用Type.ReflectionOnlyGetType()。为了加载依赖项程序集,我处理AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve事件,从项目输出路径加载特定于项目的程序集和从silverlight安装路径使用Assembly.ReflectionOnlyLoadFrom(filepath)加载silverlight基本程序集。

在使用VS2010构建项目时,这完全正常工作,但在使用MSBuild构建时失败,即C:\ WINDOWS \ Microsoft.NET \ Framework \ v4.0.30319 \ msbuild.exe“C:\ Root \ branches \ x.x.x \ clients.sln”/t:rebuild /p:Configuration=Debug“/p:Platform=Any CPU”/v:quiet /maxcpucount:1

使用MSBuild构建时,ReflectionOnlyAssemblyResolve事件将针对"System,版本=2.0.5.0,文化=neutral,PublicKeyToken=7cec85d7bea7798e"触发,并且该程序集从silverlight安装路径中加载。但是,搜索此系统程序集时会尝试在"C:/WINDOWS/Microsoft.NET/Framework/v4.0.30319/System.DLL"中进行探测,由于主版本不匹配而失败。

以下是我代码的剥离版本,针对此问题:

  public class ValidateTypeTask : Task
  {
       public override bool Execute()
       {
            Initialize();

            List<string> typeNames = GetTypes();
            foreach (var typeName in typeNames)
            {
                var isResolved = IsResolvable(typeName);
                if (!isResolved)
                {
                    return false;
                }
            }

            CleanUp();

            return true;
        }

        private void Initialize()
        {
            AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += new ResolveEventHandler(CurrentDomain_ReflectionOnlyAssemblyResolve);
        }

        Assembly CurrentDomain_ReflectionOnlyAssemblyResolve(object sender, ResolveEventArgs args)
        {
            Assembly loadedAssembly = null;
            Exception loadedException = null;
            try
            {
                loadedAssembly = Assembly.ReflectionOnlyLoad(args.Name);//this will fail always
            }
            catch (Exception ex)
            {
                loadedException = ex;
            }

            if (loadedAssembly != null)
            {
                return loadedAssembly;
            }

            string assemblyPath = string.Empty;
            var assemblyName = new AssemblyName(args.Name);
            if (args.Name.StartsWith("System"))
            {
                assemblyPath = @"c:\Program Files\Reference Assemblies\Microsoft\Framework\Silverlight\v4.0";
            }
            else
            {
                assemblyPath = @"C:\Root\branches\x.x.x\bin\debug";
            }
            assemblyPath = string.Format(@"{0}\{1}.dll", assemblyPath, assemblyName.Name);
            if (File.Exists(assemblyPath))
            {
                loadedAssembly = Assembly.ReflectionOnlyLoadFrom(assemblyPath);
            }

            if (loadedAssembly == null)
            {
                throw loadedException;
            }

            return loadedAssembly;
        }
        private void CleanUp()
        {
            AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve -= new ResolveEventHandler(CurrentDomain_ReflectionOnlyAssemblyResolve);
        }
        private List<string> GetTypes()
        {
            return new List<string>();
        }
        private bool IsResolvable(string typeName)
        {
            Type resolvedType = null;
            try
            {
                resolvedType = Type.ReflectionOnlyGetType(typeName, true, true);
            }
            catch (Exception ex)
            {
            }

            if (resolvedType != null)
            {
                return true;
            }

            return false;
        }
    }

我收到的异常如下所示:
  System.IO.FileLoadException: Could not load file or assembly 'System, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)
  File name: 'System, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e'
  at System.RuntimeTypeHandle.GetTypeByName(String name, Boolean throwOnError, Boolean ignoreCase, Boolean reflectionOnly, StackCrawlMarkHandle stackMark, Boolean loadTypeFromPartialName, ObjectHandleOnStack type)
  at System.RuntimeTypeHandle.GetTypeByName(String name, Boolean throwOnError, Boolean ignoreCase, Boolean reflectionOnly, StackCrawlMark& stackMark, Boolean loadTypeFromPartialName)
  at System.RuntimeType.GetType(String typeName, Boolean throwOnError, Boolean ignoreCase, Boolean reflectionOnly, StackCrawlMark& stackMark)
  at System.Type.ReflectionOnlyGetType(String typeName, Boolean throwIfNotFound, Boolean ignoreCase)
  at  in c:\root\x.xx.x\BuildTasks\ValidateTypesTask.cs line xxx

  Assembly manager loaded from:  C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\clr.dll
  Running under executable  C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\msbuild.exe
  --- A detailed error log follows. 

  === Pre-bind state information ===
  LOG: User = domain\username
  LOG: DisplayName = System, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e
  (Fully-specified)
  LOG: Appbase = file:///C:/WINDOWS/Microsoft.NET/Framework/v4.0.30319/
  LOG: Initial PrivatePath = NULL
  Calling assembly : (Unknown).
  ===
  LOG: This is an inspection only bind.
  LOG: Using application configuration file: C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\msbuild.exe.Config
  LOG: Using host configuration file: 
  LOG: Using machine configuration file from C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\config\machine.config.
  LOG: Attempting download of new URL file:///C:/WINDOWS/Microsoft.NET/Framework/v4.0.30319/System.DLL.
  WRN: Comparing the assembly name resulted in the mismatch: Major Version
  ERR: Failed to complete setup of assembly (hr = 0x80131040). Probing terminated.

我也尝试将Silverlight程序集路径包含到AssemblySearchPaths项目属性中,以便让其成为探测URL的一部分,但仍然是相同的情况。


尝试更新项目引用或传递ToolsVersion=2.0。 - KMoraz
@KMoraz 我已经尝试删除和添加 System 组件引用,只是为了更新项目引用,但这并没有帮助。我不能将 ToolsVersion 设置为 2,因为目标是 Silverlight 4.0。此外,我不想设置 ToolsVersion,因为在 VS2010 中一切都运行良好。 - AnbeSivam
2个回答

1

我确定这不是我上面发布的问题的答案。但是,这是我解决它的方法。

我查阅了论坛和文档,并发现DEVENV和MSBUILD之间的基本区别。在使用MSBuild构建时,程序集解析以比Devenv更严格的方式进行。

此外,我还了解了Assembly.LoadFrom和Assembly.ReflectionOnlyLoadFrom之间的区别。Assembly.RefelectionOnlyLoadFrom只考虑文件名,而不检查版本等内容,这就是MSBuild从.NET 4.0堆栈加载System.dll的地方,而实际上它应该从Silverlight安装路径加载System.dll。

有了这些想法后,我尝试使用Assembly.LoadFrom来加载程序集,并使用Type.GetType来解析类型。这次,我遇到了StackOverflowException,因为AssemblyResolve事件反复触发了所有已加载的程序集。

这时我想到将程序集加载到一个单独的应用程序域中,以便我也可以卸载已加载的程序集。我发现this文章非常有用,并按照类似的模式解决了这个问题。

虽然我已经解决了手头的问题,但我不想因此回答获得积分。我仍然觉得我没有为MSBuild的问题提供答案。

希望这对其他人有所帮助。


0
考虑使用AppDomainIsolatedTask作为基类并重试。这样做会使处理程序集变得更加容易,因为程序集不会加载到msbuild AppDomain中。
这也将使您的事件调用与您自己的AppDomain隔离。因此,MsBuild也不能打扰您。

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