Manny
Manny

Reputation: 496

Installing NuGet packages and resolving references with a custom project type using Common Project System

I am creating a project type using Visual Studio Common Project System and hooked up the NuGet package manager. However, when I install a package it fails with the following error:

Attempting to gather dependency information for package '[PackageName]' with respect to project '[ProjectName]', targeting '.NETFramework,Version=v4.7'
Gathering dependency information took 3.23 ms
Attempting to resolve dependencies for package '[PackageName]' with DependencyBehavior 'Lowest'
Resolving dependency information took 0 ms
Resolving actions to install package '[PackageName]'
Resolved actions to install package '[PackageName]'
Retrieving package '[AssemblyName] [Version]' from '[Source]'.
Adding package '[PackageName]' to folder '[ProjectDirectory]\packages'
Added package '[PackageName]' to folder '[ProjectDirectory]\packages'
Install failed. Rolling back...
Package '[PackageName]' does not exist in project '[ProjectName]'
Removing package '[PackageName]' from folder '[ProjectDirectory]\packages'
Removed package '[PackageName]' from folder '[ProjectDirectory]\packages'
Executing nuget actions took 1.17 sec
Failed to add reference to '[AssemblyName]'.
  Unable to find a type of reference that is appropriate for this file: "[ProjectDirectory]\packages\[PackageName]\lib\net46\[AssemblyName].dll".
Time Elapsed: 00:00:01.2728560
========== Finished ==========

I decompiled the NuGet assemblies and debugged it to find that the error is occurring in NuGet.PackageManagement.VisualStudio.VsMSBuildProjectSystem.AddReferenceAsync on this line reference = References.Add(assemblyFullPath);. So I tried executing the same code and got the same error when trying "System" and "[FullAssemblyPath]".

Unable to find a type of reference that is appropriate for this file: [Assembly].

VsMSBuildProjectSystem Source Code

I tried debugging a normal Visual Basic project and found that it executes a slightly different command, References3.AddFiles(new[] { assemblyFullPath }, out var referencesArray);, this is because the References object can be cast to References3. It turns out that a Visual Basic project is a VSLangProj80.VSProject2 type and my custom project is a VSLangProj.VSProject type.

I also noticed that in my custom project type the references have a yellow exclamation mark beside them, because they are unresolved. I believe this is the source of the problem. I'm not sure how Visual Studio resolves references but through debugging I was able to discover that the MSBuild target ResolveAssemblyReferences is used. When I override it in the Visual Basic project file, the references also show a yellow exclamation, but even when my custom project returns the same information, the references are still unresolved. There must be other things that are used.

I wanted to confirm that it could work but to override that function I found that MSBuildNuGetProjectSystemFactory created the VsMSBuildProjectSystem class based on hard coded project type GUIDs and it's an internal class. It appears to be hooked up by exporting MSBuildNuGetProjectProvider that implements INuGetProjectProvider. I wanted to override it with my own provider and return a custom implementation with this code but unfortunately it never gets instantiated. MSBuildNuGetProjectProvider Source Code

<Export(GetType(INuGetProjectProvider))>
<Name("MSBuildNuGetProjectProvider")>
<Microsoft.VisualStudio.Utilities.Order(After:="ProjectJsonProjectProvider")>
Friend Class MyNuGetProjectProvider : Implements INuGetProjectProvider

    <ImportingConstructor>
    Public Sub New(threadingService As IVsProjectThreadingService)
        Me.New(AsyncServiceProvider.GlobalProvider, threadingService)
    End Sub

    Public Sub New(vsServiceProvider As Microsoft.VisualStudio.Shell.IAsyncServiceProvider, threadingService As IVsProjectThreadingService)

    End Sub

    Public ReadOnly Property ProjectType As RuntimeTypeHandle Implements INuGetProjectProvider.ProjectType
        Get
            Return GetType(MSBuildNuGetProject).TypeHandle
        End Get
    End Property

    Public Async Function TryCreateNuGetProjectAsync(project As IVsProjectAdapter, context As ProjectProviderContext, forceProjectType As Boolean) As Task(Of NuGetProject) Implements INuGetProjectProvider.TryCreateNuGetProjectAsync
        Dim projectSystem As New TestProjectSystem(project, context.ProjectContext)

        Await projectSystem.InitializeProperties()

        Return New MSBuildNuGetProject(projectSystem, context.PackagesPathFactory.Invoke, project.ProjectDirectory)
    End Function

End Class

So I just hacked it and used reflection to add my class to MSBuildNuGetProjectSystemFactory and my custom class gets executed, overriding the method to add the reference and instead do nothing. Adding a NuGet package succeeded, but of course the reference is not added to the project. MSBuildNuGetProjectSystemFactory Source Code

Friend Class TestProjectSystem : Inherits VsMSBuildProjectSystem

    Private mProjectAdapter As IVsProjectAdapter

    Public Sub New(a As IVsProjectAdapter, c As INuGetProjectContext)
        MyBase.New(a, c)

        Me.mProjectAdapter = a
    End Sub


    Public Overrides Async Function AddReferenceAsync(referencePath As String) As Threading.Tasks.Task
        'Dim project = TryCast(Me.mProjectAdapter.Project.Object, VSLangProj.VSProject)

        'project.References.Add(referencePath)
    End Function

End Class

So I still don't know how visual studio is resolving assemblies and I hope that once it is fixed, the NuGet package manager will work as well. I thought it might use IVsDesignTimeAssemblyResolution but I could never get my class instantiated through MEF. I'm wondering if it is handled in Visual Studio unmanaged code and no extension points exist, considering it's a different project class being created.

If that is the case then I think I can use the INuGetProjectProvider interface to override adding the reference and do it through editing the XML file directly. But why is my INuGetProjectProvider implementation never being instantiated through MEF? I tried changing the Order attribute to 0 and 1000. Does Visual Studio MEF have a scope for Nuget within its own assemblies so my class is never picked up?

Upvotes: 1

Views: 487

Answers (0)

Related Questions