Assembly binding redirect from a Class Library - visual-studio

I have a GitHub project (Test Automation Essentials) which references some Visual Studio specific assemblies (Microsoft.VisualStudio.TestTools.UITest.Extension which is part of CodedUI; but that's not significant for the question). This project is published as a NuGet package containing my class library.
I want my project to support different versions of Visual Studio, and all in all, this assembly does not have any noticeable differences between the versions of Visual Studio, so I don't anticipate any compatibility issues (it should be backward compatible anyway).
However, if I compile my project in one version of Visual Studio (e.g. 2015), when I try to reference the NuGet package from a project in a newer version of Visual Studio (e.g. 2017), when the hosting project runs I get the following exception:
System.IO.FileNotFoundException: Could not load file or assembly 'Microsoft.VisualStudio.TestTools.UITest.Extension, Version=12.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. The system cannot find the file
Note: my library references this assembly with Specific Version=False.
I found I can work around this issue by adding the following element to the app.config of the application:
<dependentAssembly>
<assemblyIdentity name="Microsoft.VisualStudio.TestTools.UITest.Extension" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="12.0.0.0-15.0.0.0" newVersion="15.0.0.0"/>
</dependentAssembly>
Note: In this particular case the executable is typically QTAgent32_40.exe which is itself part of Visual Studio, so I had to add the element to QTAgent32_40.exe.config and not actually to the project's app.config file. QTAgent32_40.exe.config already has many similar dependentAssembly elements, but for some reason not for this specific assembly.
The question:
I don't want my clients to add this setting themselves. I'd be glad if I could have such a setting specific for my class library, so that anyone who references my library automatically gets this Assembly Redirect setting. However, I didn't find a way to do that...
Does anyone knows how can I do it?

The only way to do this is to create a Nuget package for each specific version of Visual Studio.

Related

Windows Forms Designer Crashes Visual Studio, can't find System.ComponentModel.Annotations

I have a solution structured as such:
MyDataAccessLibrary.csproj (.NET Standard 2.0)
Nuget packages include
Microsoft.EntityFrameworkCore 3.1.3
System.ComponentModel.Annotations 4.7.0
MyApplication.csproj: Windows Forms app (.NET Framework 4.8), uses Package References for dependencies
System.ComponentModel.Annotation 4.7.0
In the Windows Forms application, there is a form (MySuperForm) with a property defined in MyDataAccessLibrary (that type is also an entity type referenced through the DbContext with a DbSet) that serves as a super class to a dozen or so other forms in the project. MySuperForm opens without issue in the forms designer.
The project compiles and runs without problems.
However, attempting to open any of the forms subclassed from MySuperForm gives a designer error:
Could not load File or Assembly System.ComponentModel.Annotations version 4.2.0.0.
And Visual Studio 2019 subsequently crashes and restarts (Version 4.8.03752).
My app.config file contains the following Binding Redirect:
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.ComponentModel.Annotations" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.2.1.0" newVersion="4.2.1.0" />
</dependentAssembly>
</assemblyBinding>
Which is what was generated by using the Auto-generate binding redirect option in the project properties. Note that confusingly, the 4.7.0 version of the Nuget package includes 4.2.1.0 of the actual file, and the dll copied into the bin folder shows version 4.7.26515.06.
I have tried adding the Microsoft.EntityFrameworkCore 3.1.3 package to the application even though no code in that app directly references it (package references bubble up the transitive dependency).
The same version of System.ComponentModel.Annotations is used by all referenced projects in my solution.
I have cleaned, rebuilt, cleared cache, deleted bin and obj folders repeatedly. How can I get these forms to open in the designer?

Visual Studio looks for an older assembly version on the very first run

Visual Studio Professional 2017
Version 15.7.4
I have a web project (MVC) that whenever I run it from visual studio, it opens the browser (I have reproduced it in both Chrome and IE) and shows the following error:
Could not load file or assembly 'Microsoft.Owin, Version=2.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)
However, if I refresh the browser, it goes ahead and works as intended. Technically speaking I can work around it, but it has been getting annoying and time consuming. And I have no idea what's going on.
I've tried restarting both VS and Windows (server 2012 r2), checked the GAC (found nothing), I checked all references to Owin in my project and I see nothing that references v2.1, and I even have a binding redirect on my web.config
<dependentAssembly>
<assemblyIdentity name="Microsoft.Owin" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="0.0.0.0-3.1.0.0" newVersion="3.1.0.0" />
</dependentAssembly>
I've looked into mostly how to fix the kind of error exception I am getting, but I haven't found anything similar and I am guessing there's some sort of cache or default option in VS that is trying to look for the older version on the first run, but I don't know where to look for it.
Any ideas?

How to do dll bindingRedirect in a Vsix extension?

I have an extension to VS that should use Gmail api to send mails to certain users in my company.
During development I step into a common issue with System.Net.Http.Primitives version that is somehow messed up in Google API.
The common solution for this is to put bindingRedirect in app.config to redirectall calls to a new up-to-date version of the library. Like below:
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Net.Http.Primitives" publicKeyToken="b03f5f7f11d50a3a" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-4.0.10.0" newVersion="4.0.10.0"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
However, this seems not to work in case when my output is a Vsix package. Generated Vsix doesn't even have an app.config.
I'm aware of a solution that says to add bindingRedirect to machine.config file but my extensions is used by some other people and I would rather not force them to put stuff into their machine configuration files.
Is there another solution for this?
This was answered over a year ago, but I found a better way to do it by using ProvideBindingRedirectionAttribute. This will add the binding redirects to devenv, and also determine the correct version. Details can be found here, but the relevant part here:
By using the ProvideBindingRedirection attribute, you can specify binding redirection for the installation of an upgrade to an extensible component. When you ship an extensible Visual Studio component, this attribute prevents users of the component from having to install an old version of a dependent component. If you use the ProvideBindingRedirection attribute, you don't need to manually update the exe.config file to redirect users of the old assembly version to the new version.
Adding a ProvideBindingRedirection assembly attribute is an easy way to add a binding redirection entry to the pkgdef file. The pkgdef file is used to install the extension.
The following example shows a ProvideBindingRedirection entry in the AssemblyInfo.cs or AssemblyInfo.vb file:
[assembly: ProvideBindingRedirection(AssemblyName = "ClassLibrary1",
NewVersion = "3.0.0.0", OldVersionLowerBound = "1.0.0.0",
OldVersionUpperBound = "2.0.0.0")]
Technically, the app.config belongs to the process (.exe), not to the dlls. For Visual Studio, it is the devenv.exe.config file located at C:\Program Files (x86)\Microsoft Visual Studio <version>\Common7\IDE.
But to modify that file your extension should be installed with admin rights (that is, .msi or similar installer technology). And I don't think it's a good idea to modify that file since it would affect other extensions.
One approach that you can try is to redirect binding by code somehow forcing an assembly resolution failure, subscribing to the AppDomain.AssemblyResolveEvent, to get a chance of providing the exact assembly that you want. See: http://blog.slaks.net/2013-12-25/redirecting-assembly-loads-at-runtime/
Nice info, this ProvideBindingRedirection. It however affects the Visual Studio configuration, not just the VSIX. In particular our VSIX requires redirects for NuGet assemblies, causing the package restore support in Visual Studio to fail...

How can a VS extension target multiple versions in regard to Microsoft.VisualStudio.* references?

A few extensions that I'm using are broken under VS2012 because at some point they were updated to work with VS2013, by changing the version of referenced libraries. At runtime an error like this can be produced:
Could not load file or assembly 'Microsoft.VisualStudio.Shell.12.0, Version=12.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. The system cannot find the file specified.
I see various extensions referencing multiple versions of the same library:
<Reference Include="Microsoft.VisualStudio.Shell.Interop" />
<Reference Include="Microsoft.VisualStudio.Shell.Interop.8.0, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<Reference Include="Microsoft.VisualStudio.Shell.Interop.9.0, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
Others release an extension per VS version.
Another option, according to this article, is to dynamically load the correct version.
I'd like to help fix these extensions, but what's the proper way to tackle this issue?
So the Visual Studio reference assemblies break down into a few different categories, which you should handle differently depending upon the category:
Interop assemblies: these are the ones like the Interop.* assemblies you listed in your question. Each interop assembly isn't a newer version of the "same" thing, but rather an assembly that contains all the COM interfaces that were added in that version of Visual Studio. Referencing old versions is fine, just don't reference a newer one than the lowest version of Visual Studio you want to target
Editor assemblies, Roslyn: Anything related to the core text editor (assemblies are Microsoft.VisualStudio.Text.Data, Microsoft.VisualStudio.Text.UI, Microsoft.VisualStudio.Text.UI.Wpf, and Microsoft.VisualStudio.Editor) and Roslyn Visual Studio includes assembly redirects that redirect whatever version you're referencing to the version of VS you're actually running in. Once again, target the lowest version that you intend to support.
Microsoft.VisualStudio.Shell.[version]: this one confuses people a lot. How this particular assembly works is for each version of Visual Studio that ships, a new assembly name (with the version in the assembly) is made. Then, in future versions of the Visual Studio, we ship a newer version of the assembly that you target. So again, make sure you're targeting Microsoft.VisualStudio.Shell.[version] with the lowest version you intend to support.
The tricky problem here is the VSSDK project upgrader likes to upgrade your projects to newer versions. Get used to editing MSBuild files by hand to ensure it doesn't do this, or downgrade what it already did. For the final VSIX you ship to users it's often best to either build with an older version of VS to ensure it's not picking up newer stuff by accident. If you want to only use a newer version, then you'll have to find the VS binaries from the older version you wish to use and check those into your source control system to ensure the older versions are still being picked up. Test your VSIX if you go this route as it's easy to make a mistake and reference something newer by accident.
I wrote an article discussing the various assembly versioning policies used by Visual Studio assemblies.
http://tunnelvisionlabs.github.io/vsbase/docs-master/html/edbfd3ce-43f4-4f3f-a90c-bc22bda19fae.htm
In addition, the VSSDK.* NuGet packages use dependency declarations to help you identify the version(s) of Visual Studio each extension can be used with.
The particular version of Microsoft.VisualStudio.Shell you referenced is a Versioned Assembly (per the previous article) and included in the VSSDK.Shell.12 package, with the following description:
This package provides the Visual Studio "Shell" reference assembly used by Visual Studio 2013 and newer.
To easily target both Visual Studio 2012 and Visual Studio 2013, use NuGet to manage your VS SDK dependencies, and ensure the following conditions hold:
Make sure your assembly does not have a dependency on the VSSDK.IDE.12 NuGet package. This dependency means one or more assemblies referenced by your project only work with Visual Studio 2013 and newer.
Make sure your assembly does not have a dependency on VSSDK.IDE.10Only, VSSDK.IDE.11Only, or VSSDK.IDE.12Only. These indicate that your package references one or more assemblies that only work with a particular version of Visual Studio.
Ideally you would only want to install VSSDK NuGet packages which include both the vs2012 and vs2013 tags.

Code Analysis error Could not load file or assembly 'System.Net.Http, Version=2.0.0.0 in MVC4 Web API

this problem is exactly the same as this post http://forums.asp.net/t/1807797.aspx/1?System+Net+Http+is+not+found and this one on StackOverflow
I have all the latest RTM bits, Started a new MVC 4 in .Net 4.5, added the WebAPI nuget package and now my code analysis fails with the same error as reported in the above link.
CA0058 Error Running Code Analysis CA0058 : The referenced assembly 'System.Net.Http, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' could not be found. This assembly is required for analysis and was referenced by: C:\Projects\InHouse\TimeRecorder\StopGap\TimeRec\bin\TimeRec.dll, C:\Projects\InHouse\TimeRecorder\StopGap\packages\Microsoft.AspNet.WebApi.Core.4.0.20710.0\lib\net40\System.Web.Http.dll. [Errors and Warnings] - (Global)
From what I can find this seemed to happen with the RC versions because there was a conflict between the .NET 4.5 framework System.Net.Http and the WebApi's version of the System.Net.Http.
The other answers on the StackOverflow response talk about downgrading from .Net 4.5 to 4.0, for obvious reasons, this is not my preferred solution!
Try the following:
Depending on your Visual Studio edition navigate to:
VS 2010:
%ProgramFiles(x86)%\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop
VS 2012:
%ProgramFiles(x86)%\Microsoft Visual Studio 11.0\Team Tools\Static Analysis Tools\FxCop
Open FxCopCmd.exe.config and change AssemblyReferenceResolveMode
from StrongName to StrongNameIgnoringVersion.
Save the change and rebuild your project.
From Visual Studio 2012 and higher, instead of modifying your installation files, use the workaround specified here: Using Microsoft.Bcl.Async with Code Analysis causes errors.
I have had the same problem (couldn't build locally and remotely on azure).
This workaround helped me:
http://connect.microsoft.com/VisualStudio/feedback/details/760208/nuget-package-for-asp-net-mvc-4-web-api-does-not-reference-correct-net-4-5-assemblies#
here is the part you need:
Copy the System.Net.Http.dll and System.Net.Http.xml files contained in the packages\Microsoft.Net.Http.2.0.20710.0\lib\net40 directory to the packages\Microsoft.AspNet.WebApi.Core.4.0.20710.0\lib\net40 directory. Since the missing System.Net.Http.dll assembly is now in the same location as the referenced System.Web.Http.dll assembly, the code analysis can now properly resolve the conflicting System.Net.Http assembly.
The issue is caused because you have a dependency on a newer version of System.Net.Http, than that required by one of the other assemblies referenced.
The correct way to resolve this issue is to add dependentAssembly redirects to the app.config of offending projects. The accepted answer of disabling the errors just masks an underlying problem.
Add the following to the runtime section of app.config to remap the old version that can't be resolved to the version referenced in your project. The version numbers should obviously be updated to correspond to your situation.
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Net.Http" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="1.0.0.0-2.0.0.0" newVersion="2.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>

Resources