5

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

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

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

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

4

7 回答 7

3

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

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 |]
    }



[<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
于 2014-06-03T11:32:10.347 回答
2

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

以下是针对框架版本 2 时演示违规的示例代码:

using System.Runtime;

class Program
{
    static void Main()
    {
        GCSettings.LatencyMode = GCLatencyMode.LowLatency;
    }
}
于 2009-07-02T16:40:47.567 回答
2

这应该向您展示如何做您真正想做的事情,但我认为您应该使用我提供的 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();
    }
于 2009-07-02T19:11:25.650 回答
2

为什么不直接针对您的项目或解决方案文件调用 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 所做的。

于 2009-11-20T18:40:13.470 回答
1

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

Microsoft.Build.Shared.TypeLoader

有一个方法叫做

internal LoadedType Load(string typeName, AssemblyLoadInfo assembly);

这可能有帮助?

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

于 2009-07-03T20:10:12.490 回答
0

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

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

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

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

于 2009-06-26T19:12:25.990 回答
0

这可能会有所帮助:Resolving Binary References in MSBuild

于 2009-07-03T20:04:00.957 回答