模仿msbuild进程的程序集解析

我正在编写一个validation工具,用于检查项目中引用的文件版本。 我想使用MSBuild使用的相同解析过程。

例如,Assembly.Load(..)需要一个完全限定的程序集名称。 但是,在项目文件中,我们可能只有“System.Xml”之类的东西。 MSBuild可能使用项目的目标框架版本和其他一些启发式来决定要加载哪个版本的System.Xml。

你将如何模仿(或直接使用)msbuild的assembly解决过程?

换句话说,在运行时,我想获取字符串“System.Xml”,以及.csproj文件中找到的其他信息,并找到msbuild将找到的相同文件。

如果您希望与之兼容的Framework版本而不是目标3.5,则Visual Studio 2008 SP1和FxCop 1.36 RTM添加了规则CA 1903:仅使用目标框架中的API以确保您与目标框架版本保持兼容。 将该规则作为error handling并将其视为错误将导致您的Build失败并提供您想要的行为。

以下是在您定位框架版本2时演示违规的示例代码:

using System.Runtime; class Program { static void Main() { GCSettings.LatencyMode = GCLatencyMode.LowLatency; } } 

我今天遇到了这个问题,我发现这篇关于如何做到这一点的老博文:

http://blogs.msdn.com/b/jomo_fisher/archive/2008/05/22/programmatically-resolve-assembly-name-to-full-path-the-same-way-msbuild-does.aspx

我试了一下,效果很好! 我修改了代码以尽可能找到4.5.1版本的程序集,这就是我现在所拥有的:

 #if INTERACTIVE #r "Microsoft.Build.Engine" #r "Microsoft.Build.Framework" #r "Microsoft.Build.Tasks.v4.0" #r "Microsoft.Build.Utilities.v4.0" #endif open System open System.Reflection open Microsoft.Build.Tasks open Microsoft.Build.Utilities open Microsoft.Build.Framework open Microsoft.Build.BuildEngine /// Reference resolution results. All paths are fully qualified. type ResolutionResults = { referencePaths:string array referenceDependencyPaths:string array relatedPaths:string array referenceSatellitePaths:string array referenceScatterPaths:string array referenceCopyLocalPaths:string array suggestedBindingRedirects:string array } let resolve (references:string array, outputDirectory:string) = let x = { new IBuildEngine with member be.BuildProjectFile(projectFileName, targetNames, globalProperties, targetOutputs) = true member be.LogCustomEvent(e) = () member be.LogErrorEvent(e) = () member be.LogMessageEvent(e) = () member be.LogWarningEvent(e) = () member be.ColumnNumberOfTaskNode with get() = 1 member be.ContinueOnError with get() = true member be.LineNumberOfTaskNode with get() = 1 member be.ProjectFileOfTaskNode with get() = "" } let rar = new ResolveAssemblyReference() rar.BuildEngine <- x rar.IgnoreVersionForFrameworkReferences <- true rar.TargetFrameworkVersion <- "v4.5.1" rar.TargetedRuntimeVersion <- "v4.5.1" rar.TargetFrameworkDirectories <- [||] //[|@"C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.5\"|] rar.Assemblies <- [|for r in references -> new Microsoft.Build.Utilities.TaskItem(r) :> ITaskItem|] rar.AutoUnify <- true rar.SearchPaths <- [| "{CandidateAssemblyFiles}" "{HintPathFromItem}" "{TargetFrameworkDirectory}" // "{Registry:Software\Microsoft\.NetFramework,v3.5,AssemblyFoldersEx}" "{AssemblyFolders}" "{GAC}" "{RawFileName}" outputDirectory |] rar.AllowedAssemblyExtensions <- [| ".exe"; ".dll" |] rar.TargetProcessorArchitecture <- "x86" if not (rar.Execute()) then failwith "Could not resolve" { referencePaths = [| for p in rar.ResolvedFiles -> p.ItemSpec |] referenceDependencyPaths = [| for p in rar.ResolvedDependencyFiles -> p.ItemSpec |] relatedPaths = [| for p in rar.RelatedFiles -> p.ItemSpec |] referenceSatellitePaths = [| for p in rar.SatelliteFiles -> p.ItemSpec |] referenceScatterPaths = [| for p in rar.ScatterFiles -> p.ItemSpec |] referenceCopyLocalPaths = [| for p in rar.CopyLocalFiles -> p.ItemSpec |] suggestedBindingRedirects = [| for p in rar.SuggestedRedirects -> p.ItemSpec |] } [] let main argv = try let s = resolve([| "System" "System.Data" "System.Core, Version=4.0.0.0" "Microsoft.SqlServer.Replication" |], "") printfn "%A" s.referencePaths finally ignore (System.Console.ReadKey()) 0 

这应该告诉你如何做你真正想要的,但我认为你应该使用我提供的FXCop答案。

 static void Main() { string targetFile = @"test.csproj"; XDocument xmlDoc = XDocument.Load(targetFile); XNamespace ns = "http://schemas.microsoft.com/developer/msbuild/2003"; var references = from reference in xmlDoc.Descendants(ns + "ItemGroup").Descendants(ns + "Reference") select reference.Attribute("Include").Value; foreach (var reference in references) { Assembly.LoadWithPartialName(reference); } foreach (var item in AppDomain.CurrentDomain.GetAssemblies()) { var assemblyVersion = ((AssemblyFileVersionAttribute)item.GetCustomAttributes(typeof(AssemblyFileVersionAttribute), true)[0]).Version.ToString(); Console.WriteLine("\r\nFullname:\t{0}\r\nFileVersion:\t{1}", item.FullName, assemblyVersion); } Console.WriteLine("\r\nPress any key to continue"); Console.ReadKey(); } 

为什么不直接针对项目或解决方案文件调用msbuild,将其传递给/ v:d扩展名,并解析输出文件以获取所需信息? 例如,对于每个程序集解析,您将看到以下内容:

 主要参考“System.Data,Version = 2.0.0.0,Culture = neutral,PublicKeyToken = b77a5c561934e089”。
      已解决的文件路径为“c:\ WINNT \ Microsoft.NET \ Framework \ v2.0.50727 \ System.Data.dll”。
      在搜索路径位置“{TargetFrameworkDirectory}”中找到参考。
          对于SearchPath“{TargetFrameworkDirectory}”。
          考虑“C:\ Program Files \ Reference Assemblies \ Microsoft \ Framework \ v3.5 \ System.Data.exe”,但它不存在。
          考虑“C:\ Program Files \ Reference Assemblies \ Microsoft \ Framework \ v3.5 \ System.Data.dll”,但它不存在。
          考虑“C:\ Program Files \ Reference Assemblies \ Microsoft \ Framework \ v3.0 \ System.Data.exe”,但它不存在。
          考虑“C:\ Program Files \ Reference Assemblies \ Microsoft \ Framework \ v3.0 \ System.Data.dll”,但它不存在。
          考虑“c:\ WINNT \ Microsoft.NET \ Framework \ v3.5 \ System.Data.exe”,但它不存在。
          考虑“c:\ WINNT \ Microsoft.NET \ Framework \ v3.5 \ System.Data.dll”,但它不存在。
          考虑“c:\ WINNT \ Microsoft.NET \ Framework \ v3.0 \ System.Data.exe”,但它不存在。
          考虑“c:\ WINNT \ Microsoft.NET \ Framework \ v3.0 \ System.Data.dll”,但它不存在。
          考虑“c:\ WINNT \ Microsoft.NET \ Framework \ v2.0.50727 \ System.Data.exe”,但它不存在。
      此引用不是“CopyLocal”,因为它是必备文件。

或者,MSBuild将解决程序集的任务委派给Microsoft.Build.Tasks.v3.5程序集中的Microsoft.Build.Tasks.ResolveAssemblyReference类(在我的情况下,构建针对3.5框架)。 您可以解析项目文件并使用适当的(元)数据提供ResolveAssemblyReference的实例,并让它为您执行解决方案 – 看起来很完美,因为这正是MSBuild所做的。

如果您自己获得Reflector的免费副本,则可以检查MSBuild.exe文件本身的内部。 我注意到有一节课

 Microsoft.Build.Shared.TypeLoader 

有一个叫做的方法

 internal LoadedType Load(string typeName, AssemblyLoadInfo assembly); 

哪个可能有帮助?

无论如何,使用reflection器,您可以获得代码,并希望直接重用系统。

要直接模仿CLR解析过程,您可以编写自定义MSBuild任务,尽管我看不到它会实现什么。

MSBuild无法解析程序集。 它们由CLR解决。 本文介绍运行时如何解析程序集: http : //msdn.microsoft.com/en-us/library/yx7xezcf.aspx

当您在Visual Studio中时,系统程序集来自文件系统,但是当它们在运行时加载时,它们来自GAC。 http://p3net.mvps.org/Topics/Basics/IntegratingGACWithVS.aspx

如果您还有疑问请澄清。

这可能会有所帮助: 在MSBuild中解析二进制引用