The plug-in (let us call it PLUGIN) I am developing is using two assembly files from the main software (let as call it PARENT) it is written for. As soon as the PARENT is updated to a new version (and it happens several times a week) I want my PLUGIN to load the new versions of the dependencies dynamically without forcing me to re-compile.
The PARENT loads its plug-ins as source code files and compiles them just in time. Since I want my code to be in a DLL my Loader.cs file calls functions from my DLL via reflection.
The following is the code of the Loader.cs.
// error handling removed for better readability
public Loader()
{
assembly = Assembly.LoadFile(dllPath);
type = assembly.GetType("PLUGIN.PLUGINStarter");
instance = Activator.CreateInstance(type);
}
public override void Dispose()
{
type.InvokeMember("Dispose", BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public, null, instance, null);
base.Dispose();
}
public override void OnButtonPress()
{
type.InvokeMember("ShowForm", BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public, null, instance, null);
}
Now the PLUGINStarter class in PLUGIN namespace looks as follows.
class PLUGINStarter
{
private PLUGIN plugin = null;
/// <summary>
/// This is loading PARENT.exe and PARENTSomeOtherFile.dll dependencies.
/// </summary>
public PLUGINStarter()
{
AppDomain.CurrentDomain.AssemblyResolve += (sender, eventArgs) =>
{
var fullAssemblyName = new AssemblyName(eventArgs.Name);
// this is not executed when PARENT version changes
MessageBox.Show(fullAssemblyName.Name, "Loading assembly...");
if (fullAssemblyName.Name.Equals("PARENT"))
{
// AppDomain.CurrentDomain.FriendlyName will handle the case where PARENT.exe is re-named
var found = Assembly.LoadFile(Path.Combine(Environment.CurrentDirectory, AppDomain.CurrentDomain.FriendlyName));
return found;
}
else if (fullAssemblyName.Name.Equals("PARENTSomeOtherFile"))
{
var found = Assembly.LoadFile(Path.Combine(Environment.CurrentDirectory, "PARENTSomeOtherFile.dll"));
return found;
}
else
{
return null;
}
};
Initialize();
}
[MethodImpl(MethodImplOptions.NoInlining)]
private void Initialize()
{
// the PARENT's assemblies are referenced in the PLUGIN class
plugin = new PLUGIN();
}
public void ShowForm()
{
plugin.ShowForm();
}
public void Dispose()
{
plugin.Dispose();
}
}
When the PARENT is updated to a new version the event is not fired. Why?
EDIT #1
For clarification: PARENT loads (compiles it just in time) Loader.cs which loads the PLUGIN.dll which depends on assemblies from PARENT (and mainly PARENT.exe itself).
EDIT #2
The PARENT software is updated manually. The user downloads it from the internet (the web site of the product). Then the user copies my PLUGIN.dll into the "Plugins" directory of the PARENT.
Then I am able to catch the following exception in Loader.cs.
[07:55:19.822 D] [PLUGIN] Loading DLL 'C:\PNT\PARENTNew\Plugins\PLUGIN\PLUGIN.dll'.
[07:55:19.826 D] [PLUGIN] Exception loading assembly 'System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.IO.FileLoadException: Could not load file or assembly 'PARENT, Version=6.2.8113.191, Culture=neutral, PublicKeyToken=21a554ab5c01ae50' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)
at PLUGIN.PLUGINStarter..ctor()
--- End of inner exception stack trace ---
at System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean noCheck, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor, Boolean& bNeedSecurityCheck)
at System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark)
at System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark)
at System.Activator.CreateInstance(Type type, Boolean nonPublic)
at System.Activator.CreateInstance(Type type)
at PLUGINLoader.Loader..ctor() in c:\PNT\PARENTNew\Plugins\PLUGIN\Loader.cs:line 42'.
EDIT #3
I believe this is possible as described in Is it possible to replace a reference to a strongly-named assembly with a "weak" reference? but for some strange reason it fails in my application.
EDIT #4
I solved the problem by removing PLUGINStarter and moving the assembly resolving code to the constructor of Loader.cs. Now everything resolves nicely despite wrong assembly versions.
As described in EDIT #4 the assembly event was not registered soon enough.
I solved the problem by removing PLUGINStarter and moving the assembly resolving code to the constructor of Loader.cs. Now everything resolves nicely despite wrong assembly versions.