C# 模拟msbuild进程的程序集解析

C# 模拟msbuild进程的程序集解析,c#,msbuild,assembly-resolution,C#,Msbuild,Assembly Resolution,我正在编写一个验证工具,用于检查项目中引用的文件的版本。我希望使用与MSBuild相同的解析过程 例如,Assembly.Load(..)需要完全限定的程序集名称。但是,在项目文件中,我们可能只有类似“System.Xml”的内容。MSBuild可能使用项目的目标框架版本和其他一些启发式方法来决定加载哪个版本的System.Xml 您将如何模仿(或直接使用)msbuild的程序集解析过程 换句话说,在运行时,我希望获取字符串“System.Xml”,以及在.csproj文件中找到的其他信息,并找

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

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

您将如何模仿(或直接使用)msbuild的程序集解析过程


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

要直接模拟CLR解析过程,您可以编写自定义msbuild任务,尽管我不知道它将实现什么

MSBuild不解析程序集。这些问题由CLR解决。本文介绍运行时如何解析程序集:

在VisualStudio中,系统程序集来自文件系统,但在运行时加载时,它们来自GAC


如果您仍有疑问,请澄清。

如果您的目标是希望与之兼容的框架版本,而不是目标版本3.5,Visual Studio 2008 SP1和FxCop 1.36 RTM添加了规则,以确保您与目标框架版本保持兼容。打开该规则并将其视为错误将导致构建失败,并提供所需的行为

以下是示例代码,演示了当您以framework版本2为目标时的违规行为:

using System.Runtime;

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

这应该向您展示如何做您真正想要的事情,但我认为您应该使用我提供的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.exe文件本身的内部结构。我注意到有一节课

Microsoft.Build.Shared.TypeLoader
它有一个名为

internal LoadedType Load(string typeName, AssemblyLoadInfo assembly);
哪个可能有用


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

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

Primary reference "System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089". Resolved file path is "c:\WINNT\Microsoft.NET\Framework\v2.0.50727\System.Data.dll". Reference found at search path location "{TargetFrameworkDirectory}". For SearchPath "{TargetFrameworkDirectory}". Considered "C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.5\System.Data.exe", but it didn't exist. Considered "C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.5\System.Data.dll", but it didn't exist. Considered "C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0\System.Data.exe", but it didn't exist. Considered "C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0\System.Data.dll", but it didn't exist. Considered "c:\WINNT\Microsoft.NET\Framework\v3.5\System.Data.exe", but it didn't exist. Considered "c:\WINNT\Microsoft.NET\Framework\v3.5\System.Data.dll", but it didn't exist. Considered "c:\WINNT\Microsoft.NET\Framework\v3.0\System.Data.exe", but it didn't exist. Considered "c:\WINNT\Microsoft.NET\Framework\v3.0\System.Data.dll", but it didn't exist. Considered "c:\WINNT\Microsoft.NET\Framework\v2.0.50727\System.Data.exe", but it didn't exist. This reference is not "CopyLocal" because it's a prerequisite file. 主要参考“System.Data,Version=2.0.0.0,Culture=neutral,PublicKeyToken=b77a5c561934e089”。 解析的文件路径为“c:\WINNT\Microsoft.NET\Framework\v2.0.50727\System.Data.dll”。 在搜索路径位置{TargetFrameworkDirectory}找到引用。 对于搜索路径“{TargetFrameworkDirectory}”。 考虑了“C:\Program Files\Reference Assembly\Microsoft\Framework\v3.5\System.Data.exe”,但它不存在。 考虑了“C:\Program Files\Reference Assembly\Microsoft\Framework\v3.5\System.Data.dll”,但它不存在。 考虑了“C:\Program Files\Reference Assembly\Microsoft\Framework\v3.0\System.Data.exe”,但它不存在。 考虑了“C:\Program Files\Reference Assembly\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所做的。

我今天遇到了这个问题,我发现了一篇关于如何执行此操作的老博文:

我试过了,效果很好!我修改了代码,以便在可能的情况下查找程序集的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 |]
    }



[<EntryPoint>]
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
#如果是交互式的
#r“Microsoft.Build.Engine”
#r“Microsoft.Build.Framework”
#r“Microsoft.Build.Tasks.v4.0”
#r“Microsoft.Build.Utilities.v4.0”
#恩迪夫
开放系统
开放系统。反射
打开Microsoft.Build.Tasks
打开Microsoft.Build.Utilities
打开Microsoft.Build.Framework
打开Microsoft.Build.BuildEngine
///参考分辨率结果。所有路径都是完全限定的。
类型解析结果={
引用路径:字符串数组
ReferenceDependencyPath:字符串数组
关联路径:字符串数组
引用卫星路径:字符串数组
ReferencesCatterPath:字符串数组
ReferenceCopylocalPath:字符串数组
suggestedBindingRedirects:字符串数组
}
让我们解析(引用:字符串数组,outputDirectory:string)=
设x={new IBuildEngine与
成员be.BuildProjectFile(projectFileName、targetNames、globalProperties、TargetOutput)=true
成员be.LogCustomEvent(e)=()
成员be.LogErrorEvent(e)=()
成员be.LogMessageEvent(e)=()
成员be.LogWarningEvent(e)=()
get()为1的成员be.ColumnNumberOfTaskNode
成员be.ContinueOnError,get()=true
get()为1的成员be.LineNumberOfTaskNode
get()为“”的成员be.ProjectFileOfTaskNode
设rar=new resolveSassemblyReference()

我感兴趣的是程序集在编译时来自何处。在运行时,我希望在XML中查找版本属性为“System.XML”的引用,并找到msbuild找到的相同程序集