Programmatically Resolve Assembly Name to Full Path the Same Way MSBuild Does

Programmatically Resolve Assembly Name to Full Path the Same Way MSBuild Does

  • Comments 7

Jomo Fisher—Every once in a while I find I need to turn and assembly name like “System” or “System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL” into a path to the actual file for that assembly. This is a tricky problem considering the assembly name might be for a product install in Program Files or elsewhere. MSBuild already has rules for doing this resolution. Here’s how to call that code directly (written in F#):

#light

 

#r "c:\Windows\Microsoft.NET\Framework\v2.0.50727\Microsoft.Build.Tasks.dll"

#r "c:\Windows\Microsoft.NET\Framework\v2.0.50727\Microsoft.Build.Utilities.dll"

#r "c:\Windows\Microsoft.NET\Framework\v2.0.50727\Microsoft.Build.Framework.dll"

#r "c:\Windows\Microsoft.NET\Framework\v2.0.50727\Microsoft.Build.Engine.dll"

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 = {

    /// Paths to primary references

    referencePaths:string array

    /// Paths to dependencies

    referenceDependencyPaths:string array

    /// Paths to related files (like .xml and .pdb)

    relatedPaths:string array

    /// Paths to satellite assemblies used for localization.

    referenceSatellitePaths:string array

    /// Additional files required to support multi-file assemblies.

    referenceScatterPaths:string array

    /// Paths to files that reference resolution recommend be copied to the local directory

    referenceCopyLocalPaths:string array

    /// Binding redirects that reference resolution recommends for the app.config file.

    suggestedBindingRedirects:string array

    }

 

 

let Resolve(references:string array, outputDirectory:string) =

    let x = { new IBuildEngine with

                member be.BuildProjectFile(projectFileName, targetNames, globalProperties, argetOutputs) = 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.TargetFrameworkDirectories <- [|@"C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.5\"|]

    rar.AllowedRelatedFileExtensions <- [| ".pdb"; ".xml"; ".optdata" |]

    rar.FindRelatedFiles <- true

    rar.Assemblies <- [|for r in references -> new Microsoft.Build.Utilities.TaskItem(r):>ITaskItem|]

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

    }

   

try

    let s = Resolve([| "System"

                       "System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL"

                       "Microsoft.SqlServer.Replication"

                    |], "")

    printfn "%A" s

finally

    ignore (System.Console.ReadKey())

The result of running the code looks like this:

{referencePaths =

  [|"C:\\Program Files\\Microsoft SQL Server\\90\\SDK\\Assemblies\\Microsoft.SqlServer.Replication.dll";

    "C:\\Program Files\\Reference Assemblies\\Microsoft\\Framework\\v3.5\\System.Core.dll";

    "C:\\Windows\\assembly\\GAC_MSIL\\System\\2.0.0.0__b77a5c561934e089\\System.dll"|];

 referenceDependencyPaths =

  [|"C:\\Windows\\assembly\\GAC_MSIL\\System.DirectoryServices\\2.0.0.0__b03f5f7f11d50a3a\\System.DirectoryServices.dll";

    "C:\\Windows\\assembly\\GAC_MSIL\\System.Xml\\2.0.0.0__b77a5c561934e089\\System.Xml.dll";

    "C:\\Windows\\assembly\\GAC_32\\Microsoft.SqlServer.BatchParser\\9.0.242.0__89845dcd8080cc91\\Microsoft.SqlServer.BatchParser.dll";

    "C:\\Windows\\assembly\\GAC_MSIL\\System.Runtime.Remoting\\2.0.0.0__b77a5c561934e089\\System.Runtime.Remoting.dll";

    "C:\\Windows\\assembly\\GAC_MSIL\\Microsoft.Vsa\\8.0.0.0__b03f5f7f11d50a3a\\Microsoft.Vsa.dll";

    "C:\\Windows\\assembly\\GAC_32\\System.Data.OracleClient\\2.0.0.0__b77a5c561934e089\\System.Data.OracleClient.dll";

    "C:\\Program Files\\Microsoft SQL Server\\90\\SDK\\Assemblies\\Microsoft.SqlServer.ConnectionInfo.dll";

    "C:\\Windows\\assembly\\GAC_MSIL\\Accessibility\\2.0.0.0__b03f5f7f11d50a3a\\Accessibility.dll";

    "C:\\Windows\\assembly\\GAC_32\\System.Transactions\\2.0.0.0__b77a5c561934e089\\System.Transactions.dll";

    "C:\\Windows\\assembly\\GAC_MSIL\\System.DirectoryServices.Protocols\\2.0.0.0__b03f5f7f11d50a3a\\System.DirectoryServices.Protocols.dll";

    "C:\\Windows\\assembly\\GAC_MSIL\\System.Drawing\\2.0.0.0__b03f5f7f11d50a3a\\System.Drawing.dll";

    "C:\\Windows\\assembly\\GAC_MSIL\\System.Deployment\\2.0.0.0__b03f5f7f11d50a3a\\System.Deployment.dll";

    "C:\\Program Files\\Microsoft SQL Server\\90\\SDK\\Assemblies\\Microsoft.SqlServer.Smo.dll";

    "C:\\Windows\\assembly\\GAC_32\\System.EnterpriseServices\\2.0.0.0__b03f5f7f11d50a3a\\System.EnterpriseServices.dll";

    "C:\\Windows\\assembly\\GAC_MSIL\\System.Web.RegularExpressions\\2.0.0.0__b03f5f7f11d50a3a\\System.Web.RegularExpressions.dll";

    "C:\\Program Files\\Microsoft SQL Server\\90\\SDK\\Assemblies\\Microsoft.SqlServer.WmiEnum.dll";

    "C:\\Program Files\\Microsoft SQL Server\\90\\SDK\\Assemblies\\Microsoft.SqlServer.ServiceBrokerEnum.dll";

    "C:\\Windows\\assembly\\GAC_MSIL\\System.Configuration\\2.0.0.0__b03f5f7f11d50a3a\\System.Configuration.dll";

    "C:\\Windows\\assembly\\GAC_MSIL\\System.Web.Services\\2.0.0.0__b03f5f7f11d50a3a\\System.Web.Services.dll";

    "C:\\Windows\\assembly\\GAC_MSIL\\System.Management\\2.0.0.0__b03f5f7f11d50a3a\\System.Management.dll";

    "C:\\Windows\\assembly\\GAC_MSIL\\System.Design\\2.0.0.0__b03f5f7f11d50a3a\\System.Design.dll";

    "C:\\Windows\\assembly\\GAC_32\\System.Web\\2.0.0.0__b03f5f7f11d50a3a\\System.Web.dll";

    "C:\\Windows\\assembly\\GAC_MSIL\\System.Windows.Forms\\2.0.0.0__b77a5c561934e089\\System.Windows.Forms.dll";

    "C:\\Windows\\assembly\\GAC_MSIL\\System.Security\\2.0.0.0__b03f5f7f11d50a3a\\System.Security.dll";

    "C:\\Program Files\\Microsoft SQL Server\\90\\SDK\\Assemblies\\Microsoft.SqlServer.SmoEnum.dll";

    "C:\\Windows\\assembly\\GAC_MSIL\\System.Runtime.Serialization.Formatters.Soap\\2.0.0.0__b03f5f7f11d50a3a\\System.Runtime.Serialization.Formatters.Soap.dll";

    "C:\\Windows\\assembly\\GAC_MSIL\\Microsoft.JScript\\8.0.0.0__b03f5f7f11d50a3a\\Microsoft.JScript.dll";

    "C:\\Windows\\assembly\\GAC_MSIL\\System.Configuration.Install\\2.0.0.0__b03f5f7f11d50a3a\\System.Configuration.Install.dll";

    "C:\\Windows\\assembly\\GAC_MSIL\\Microsoft.VisualC\\8.0.0.0__b03f5f7f11d50a3a\\Microsoft.VisualC.dll";

    "C:\\Program Files\\Microsoft SQL Server\\90\\SDK\\Assemblies\\Microsoft.SqlServer.SqlEnum.dll";

    "C:\\Windows\\assembly\\GAC_32\\System.Data\\2.0.0.0__b77a5c561934e089\\System.Data.dll";

    "C:\\Windows\\assembly\\GAC_MSIL\\System.Drawing.Design\\2.0.0.0__b03f5f7f11d50a3a\\System.Drawing.Design.dll";

    "C:\\Program Files\\Microsoft SQL Server\\90\\SDK\\Assemblies\\Microsoft.SqlServer.Rmo.dll";

    "C:\\Windows\\assembly\\GAC_MSIL\\System.Data.SqlXml\\2.0.0.0__b77a5c561934e089\\System.Data.SqlXml.dll";

    "C:\\Windows\\assembly\\GAC_MSIL\\System.ServiceProcess\\2.0.0.0__b03f5f7f11d50a3a\\System.ServiceProcess.dll";

    "C:\\Program Files\\Microsoft SQL Server\\90\\SDK\\Assemblies\\Microsoft.SqlServer.RegSvrEnum.dll"|];

 relatedPaths = [||];

 referenceSatellitePaths = [||];

 referenceScatterPaths =

  [|"C:\\Windows\\assembly\\GAC_32\\System.EnterpriseServices\\2.0.0.0__b03f5f7f11d50a3a\\System.EnterpriseServices.Wrapper.dll"|];

 referenceCopyLocalPaths = [||];

 suggestedBindingRedirects = [||];}

This posting is provided "AS IS" with no warranties, and confers no rights.

 

Leave a Comment
  • Please add 4 and 1 and type the answer here:
  • Post
  • Hi Jomo!

    This was a very timely post, thank you so much.

    Of course, as luck would have it, I'm not using F#... I need to do exactly what you've done here in C#.

    I think I understand most of your code.  The one thing that I don't get is this part:

    IBuildEngine buildengine = new Microsoft.Build.Framework.

    let x = { new IBuildEngine with

    member be.BuildProjectFile(projectFileName, targetNames, globalProperties, argetOutputs) = 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() = "" }

    To me that reads, "create a new IBuildEngine with the following properties."  Is that right?

    Is this creating an anonymous type that implements IBuildEngine?

    Do you know how one creates an IBuildEngine normally?  I can't even find a concrete type that implements IBuildEngine when I search in Reflector (and MSDN).  Any guidance would be greatly appreciated! :)

  • Matt,

    That's exactly right. F# lets you anonymously implement an interface with an 'object expression' like you see above. In C# or VB you'll need to create a named type (onymous?) that inherits from IBuildEngine and then new it up inside the resolution function.

  • Jomo,

    Thanks for the reply!  

    While I was investigating the above, I figured I'd temporarily try to interop directly.  I compiled your code with the latest F# compiler and referenced it from my project, and of course it works perfectly.  Viva la CLR.

    I'm looking forward to a fully supported release of F# so I can justify spending more real work time on it.  

    As an aside, I'm surprised there isn't a framework class available that implements IBuildEngine.  I expected to be able to do something like:

    task.BuildEngine = Engine.GlobalEngine;

    Keep up the good work, I really enjoy reading your blog.

    Matt

  • hi,

    i've put your code into VB. so far so good.

    but if i try to execute the RAR by:

    "If (Not rar.Execute) Then"

    i get an "EntryPointNotFoundException" at this point, i don't know why. any ideas?

    btw: F# seems to be really nice, fits the minimal style i like so much :)

    good bytes

  • Jomo Fisher— Last time I wrote about getting started with F# scripts. They’re the fastest way to just

  • Mood:

    Please verify that your code is referencing v2.0 of the MSBuild assemblies.  I was able to reproduce the raising of the EntryPointNotFoundException when I referenced the corresponding v3.5 assemblies.

  • Any idea why the related XML files don't appear in the result set?  I would expect to see at least the XML file for System.Core as it coexists with the assembly in the same directory.

Page 1 of 1 (7 items)