Mimicking assembly resolution of the msbuild proce

2020-07-13 10:47发布

I am writing a validation tool that checks the versions of files referenced in a project. I want to use the same resolution process that MSBuild uses.

For example, Assembly.Load(..) requires a fully-qualified assembly name. However, in the project file, we may only have something like "System.Xml". MSBuild probably uses the project's target framework version and some other heuristics to decide which version of System.Xml to load.

How would you go about mimicking (or directly using) msbuild's assembly resolution process?

In other words, at run-time, I want to take the string "System.Xml", along with other info found in a .csproj file and find the same file that msbuild would find.

7条回答
三岁会撩人
2楼-- · 2020-07-13 10:52

This should show you how to do what you really want, but I think you should use the FXCop answer I provided.

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();
    }
查看更多
女痞
3楼-- · 2020-07-13 11:02

To directly mimic the CLR resolution process you could write a custom MSBuild task although I don't see what it would achieve.

MSBuild doesn't resolve assemblies. They are resolved by the CLR. This article describes how the runtime resolves assemblies: http://msdn.microsoft.com/en-us/library/yx7xezcf.aspx

When you are in Visual Studio the System assemblies come from the filesystem, but when they are loaded at runtime they come from the GAC. http://p3net.mvps.org/Topics/Basics/IntegratingGACWithVS.aspx

If you still have questions please clarify.

查看更多
孤傲高冷的网名
5楼-- · 2020-07-13 11:03

If you get yourself a free copy of Reflector, you can examine the internals of the MSBuild.exe file itself. I notice there is a class

Microsoft.Build.Shared.TypeLoader

that has a method called

internal LoadedType Load(string typeName, AssemblyLoadInfo assembly);

which may help?

Anyway, with reflector you can get the code, and hopefully reuse the system directly.

查看更多
forever°为你锁心
6楼-- · 2020-07-13 11:05

Why not just call msbuild against your project or solution file, pass it the /v:d extension, and parse the output file for the information you want? For instance, you'll see something like the following for each assembly resolution:

  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.

Alternatively, MSBuild delegates the task of resolving assemblies to the Microsoft.Build.Tasks.ResolveAssemblyReference class from the Microsoft.Build.Tasks.v3.5 assembly (in my case, building against the 3.5 framework). You can parse the project file and supply an instance of ResolveAssemblyReference with the appropriate (meta)data, and let it perform the resolution for you - seems perfect, since that's exactly what MSBuild does.

查看更多
乱世女痞
7楼-- · 2020-07-13 11:18

If you target the Framework version you want to be compatible with instead of targeting 3.5, Visual Studio 2008 SP1 and FxCop 1.36 RTM added rule CA 1903: Use only API from targeted framework to ensure you stay compatible with the target framework version. Turning that rule on and treating it as an error will fail your Build and provide the behavior you want.

Here is sample code demonstrating a violation when you are targeting framework version 2:

using System.Runtime;

class Program
{
    static void Main()
    {
        GCSettings.LatencyMode = GCLatencyMode.LowLatency;
    }
}
查看更多
登录 后发表回答