2

I am experimenting with a plugin system. So that DLL files are read from a folder and these functions are then added to the main application.

All DLL files contain the interface IPlugin (project: Plugin):

public interface IPlugin
{
    void SetValue();
    string GetValue();
}

The Plugins are in seperate projects:

public class PluginAB : IPlugin
{
    public string GetValue()
    {
        return SomeStaticValues.StaticValues.Value;
    }

    public void SetValue()
    {
        SomeStaticValues.StaticValues.Value = "AB";
    }
}

There are two "plugins" AB and XY (which differ only in writing "XY" instead of "AB".

All they do is write a static value in another project:

public static class StaticValues
{
    public static string Value { get; set; }
}

The references looks like the following drawing:

References

The IPlugin objects from the DLL's are created with InvokeMember():

private IPlugin LoadPlugin(string iPath)
    {
        Assembly lAssambly = Assembly.LoadFrom(iPath);

        foreach (Type lType in lAssambly.GetTypes())
        {
            if (lType.GetInterfaces().Contains(typeof(IPlugin)))
            {
                return lType.InvokeMember(null, BindingFlags.CreateInstance, null, null, null) as IPlugin;
            }
        }

        return null;
    }

As you can see in the reference picture, the main app also has access to the "StaticValues" class.

If I call the methods in the main app like this:

static void Main(string[] args)
    {
        IPlugin lPluginAB = LoadPlugin("AB");
        IPlugin lPluginXY = LoadPlugin("XY");

        lPluginAB.SetValue();
        Console.WriteLine(lPluginAB.GetValue());
        lPluginXY.SetValue();
        Console.WriteLine(lPluginAB.GetValue());
        SomeStaticValues.StaticValues.Value = "Test";
        Console.WriteLine(lPluginAB.GetValue());

        Console.ReadLine();
    }

I would expect:

AB XY Test

instead I am getting:

AB XY XY

if I check directly

Console.WriteLine(SomeStaticValues.StaticValues.Value);

I am getting

Test

Why is a static value different if I access it from a loaded dll file instead of the main application?

Erik Eidt
  • 23,049
  • 2
  • 29
  • 53
moo.h
  • 33
  • 4
  • Is the `SomeStaticValues` a registered DLL or another project in your solution? If it is another project in your solution, and it doesn't have a strong name, then the instances of it loaded from different sources will be considered different dll's internally. Each project will have its own copy of that dll and they are probably being loaded multiple times from different sources. You can check this by doing `Assembly[] loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();` at the end of the method and examining it in the debugger. – Christopher Hamkins Nov 22 '21 at 15:48
  • You'll need to add `using System.Reflection;` to access the `GetAssemblies` method. – Christopher Hamkins Nov 22 '21 at 15:50

2 Answers2

1

When assemblies are loaded "manually" using the Assembly.LoadFrom(path) method, they will normally also retrieve their referenced assemblies. Unless they are registered and/or present in the central repository, the referenced assemblies would come from the same folder in which the loaded dll itself was located.

If the main method also references such an assembly, it will retrieve it from its own folder which I suspect in your case is different than the source of the plugins.

Internally, the dll's loaded from different locations are treated as different, separate assemblies, not as the same one, so they will each have their own static variables.

You can check if this is happening with the following code:

using System.Reflection;
using System.Text;

        public static string reportCurrentlyLoadedAssemblies()
        {
            StringBuilder sb = new StringBuilder();
            Assembly[] loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();
            foreach (Assembly ass in loadedAssemblies)
            {
                sb.Append(ass.FullName);
                sb.Append(";");
                if (!ass.IsDynamic)
                {
                    sb.Append(ass.CodeBase);
                    sb.Append(";");
                }
                sb.Append(ass.Location);
                sb.AppendLine(";");
            }
            return sb.ToString();
        }

Call this method at the end of your Main method and examine the result.

In such cases I have found that pre-loading the common assemblies can prevent the plugins from retrieving their own versions. You can pre-load them explicitly by calling Assembly.LoadFrom(path) with your SomeStaticValues at the start of the Main method; perhaps accessing SomeStaticValues.StaticValues.Value at the start would even be sufficient.

Another possibility would be to arrange for the dll's of the plugins to be present in the target folder of the Main project and load them from there so that they would use the same source for SomeStaticValues as the Main method.

Christopher Hamkins
  • 1,442
  • 9
  • 18
1

In the end changing

Assembly lAssambly = Assembly.LoadFrom(lFile.FullName);

to

Assembly lAssambly = Assembly.LoadFile(lFile.FullName);

did the trick.

Found it here

moo.h
  • 33
  • 4