I have a plugin P
extending and application A
(.NET40) that I have no control over.
The P assembly (.NET40) has a shared dependency D
(.NET35).
Both P and D depend on FSharp.Core, but different versions:
P
is compiled against FSharp.Core, Version=4.4.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
D
is compiled against FSharp.Core, Version=2.3.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
Only FSharp.Core, Version=4.4.0.0 is deployed and I subscribe to AppDomain.AssemblyResolve to load the deployed assemblies.
While I'm testing on my machine where both FSharp.Core versions are installed in the GAC, they both end up being loaded with the plugin.
My understanding is that a binding redirect would be the solution here but how can it be done without access to the app.config?
In .NET, binding redirects are there to handle version conflicts or version redirection among referenced assemblies.
You can enable automatic binding redirection if your app targets older versions of the .NET Framework. You can override this default behavior by providing binding redirection information in the app.config file for any assembly, or by turning off the binding redirection feature.
Specifying assembly binding in configuration files. You use the same XML format to specify binding redirects whether it’s in the app configuration file, the machine configuration file, or the publisher policy file. To redirect one assembly version to another, use the <bindingRedirect> element.
If an assembly is redirected in the machine's configuration file, called machine.config, all apps on that machine that use the old version are directed to use the new version. The machine configuration file overrides the app configuration file and the publisher policy file.
You could deploy D as a publisher policy assembly.
The benefit of this approach is that client directories do not need to contain *.config files to redirect to a newer version. Publisher policy allows the publisher of an assembly to install a binary version of a *.config file into the GAC (along with the assembly). This way the CLR will be able to perform the requested redirection at the level of the GAC.
If you want to bypass the publisher policy for a certain app, you can specify so in the app’s *.config file using the <publisherPolicy>
element.
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<runtime>
<assemblyBinding xmlns=“urn:schemas-microsoft-com:asm.v1”>
<publisherPolicy apply="no" />
</assemblyBinding>
</runtime>
</configuration>
Yes, indeed. If you are writing a plugin, an app.config file is useless for redirecting assemblies. The plugin will look 1st to the machine.config file on the user's computer, then look to the *.config file of the main program.
Here is the two-step process I used for carrying out assembly-binding redirecting in a plugin scenario when writing a plugin for SDL Trados 2017--
Step One: Use a try-catch statement in the plugin itself to discover the information about the assembly which is failing to load.
In my case, I suspected one of the handful of assemblies required to create a Google Cloud AutoML client was to blame, so I put a try-catch statement around the point where the plugin first tries to create a Google Cloud AutoML client:
try
{
client = AutoMlClient.Create();
}
catch (Exception err)
{
using (System.IO.StreamWriter file = new System.IO.StreamWriter("C:/Desktop/log.txt", true))
{
file.WriteLine(err.ToString());
}
}
When I checked the "log.txt" file created during the error, I found the following information:
System.IO.FileNotFoundException: Could not load file or assembly 'Google.Apis.Auth, Version=1.41.1.0, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab' or one of its dependencies. The system cannot find the file specified.
So! It was clear that, in the process of creating an AutoML client, the plugin was trying to find.Google.Apis.Auth version 1.41.1.0. However, in order to get my plugin to compile correctly, I had to install Google.Apis.Auth version 1.42.0.0 with NuGet. Hence the need for the assembly binding redirect.
Step Two: Add an event handler related to that particular assembly which will change it's version / public key token information before loading it. At the very beginning of the program--where the main form window of the plugin is initialized, I added this code:
public Main_form()
{
InitializeComponent();
Version V14200 = new Version("1.42.0.0");
RedirectAssembly("Google.Apis.Auth", V14200, "4b01fa6e34db77ab");
}
public static void RedirectAssembly(string assembly_name, Version targetVersion, string publicKeyToken)
{
ResolveEventHandler handler = null;
handler = (sender, args) => {
//gets the name of the assembly being requested by the plugin
var requestedAssembly = new AssemblyName(args.Name);
//if it is not the assembly we are trying to redirect, return null
if (requestedAssembly.Name != assembly_name)
return null;
//if it IS the assembly we are trying to redirect, change it's version and public key token information
requestedAssembly.Version = targetVersion;
requestedAssembly.SetPublicKeyToken(new AssemblyName("x, PublicKeyToken=" + publicKeyToken).GetPublicKeyToken());
requestedAssembly.CultureInfo = CultureInfo.InvariantCulture;
//finally, load the assembly
return Assembly.Load(requestedAssembly);
};
AppDomain.CurrentDomain.AssemblyResolve += handler;
}
So basically, you have to get information from the plugin (using a try-catch statement) about which assembly is failing to load. Then, you have to add an event handler, which will go into effect when the assembly in question begins to load.
In my case, I knew that Google.Apis.Auth was the problem--the plugin wanted to load version 1.41.1.0, but my plugin contained version 1.42.0.0. When the plugin begins looking for Google.Apis.Auth (1.41.1.0), the event handler steps in and changes the version number, so the plugin loads version 1.42.0.0.
Having never had any formal training in computer science or programming, I don't know how robust/recommendable this solution is, but it worked for me.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With