How to manage classes from a loaded DLL?

2019-09-19 08:16发布

问题:

How can I get a class into a variable to call function and get properties?
I have been told to look into reflection but I don't get how it's done when the DLL was not known at compile time.

To be clear: I have a 'main' class which loads an DLL and I want to create an instance of a class within the DLL and directly get properties.

回答1:

You should do something like this:

Assembly asm = Assembly.Load("DLL File Path"); //load DLL from file
Type t = asm.GetType("Test.ExternalDllTest"); //fully qualified name
dynamic oDynamic = Activator.CreateInstance(t, args);//create an instance of specified type, and assign it to DYNAMIC object

EDIT

 oDynamic.SomeMethod(); //call the method of your type. Being DYNAMIC it will route the call to correct method.

Naturally, the DLL has to be a managed DLL (so written in .NET language)

I didn't compile this, honestly, but basically this is an idea of how to do that.

Here also an example, that may help.



回答2:

In case you are talking about another .NET dll you can use this to load the assembly and get all the types in there:

 var asm = Assembly.LoadFile("yourassembly.dll");
 foreach (var t in asm.GetTypes())
 {
      Console.WriteLine("Type: {0}", t.Name);
 }

You can instantiate an object with either the Activator:

 Activator.CreateInstance(t, additional arguments);

or you can get a list of all public constructors for that type with GetConstructors:

 var constructors = t.GetConstructors();

However unless you know what type you are looking for and what its constructor parameters are it is a bit pointless to try to instatiate and use it.



回答3:

Example. Here is a method I use that searches a plug-in directory tree and returns a list of WPF ValueConverter classes...

private static List<IValueConverter> GetValueConverters( string rootDirectoryName)
{
    List<IValueConverter> result = new List<IValueConverter>();
    string[] exts = new string[]{"*.exe", "*.dll"};
    DirectoryInfo di = new DirectoryInfo(rootDirectoryName);
    foreach(string ext in exts)
    {
        foreach(FileInfo fi in (di.GetFiles(ext, SearchOption.AllDirectories)))
        {
            Assembly a = Assembly.LoadFrom(fi.FullName);
            try
            {
                List<Type> ts = a.GetExportedTypes().ToList();
                foreach (Type t in ts)
                {
                    var d2 = t.GetInterfaces().Where(q => q.Name == "IValueConverter");
                    if (d2.Count() > 0)
                    {
                        result.Add(Activator.CreateInstance(t) as IValueConverter);
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
    }
    return result;
}

The code searches through the directory tree for files matching 'dll' and 'exe'. When it finds one, it attempts to load it and then looks to see if there's any WPF ValueConverters in it.

If it finds one, the code creates an instances and adds it to the list, and ultimately returns the list. Of course the 'dll's' and 'exe's' must be in the managed world. And if you were interested in classes other than ValueConverters, you would have to change it accordingly.

This is a purpose built method (i.e., I know what's going to happen with the result) and the code is given here only as an example...



回答4:

Write your self an Interface (IKnowAboutSomething) that is accessible via your Loaded DLL and also your Loading Program.

Scan through your Loaded DLL and find the classes which implement this interface.

Then you can use Activator.CreateInstance to create an instance of the Types you found (where you know the interface)

Now you just call methods on your IKnowAboutSomething.GetThingINeed() etc and you can interact with things you don't know about at compile time. (Well you know a little bit because they are using the Interface and therefore have an agreement)

Place this code in an External DLL (eg Core.Dll) that is accessible by both projects.

using System.IO;
public interface ISettings
{
  /// <summary>
  /// Will be called after you Setup has executed if it returns True for a save to be performed. You are given a Stream to write your data to.
  /// </summary>
  /// <param name="s"></param>
  /// <remarks></remarks>
  void Save(Stream s);
  /// <summary>
  /// Will be called before your Setup Method is to enable the loading of existing settings. If there is no previous configuration this method will NOT be called
  /// </summary>
  /// <param name="s"></param>
  /// <remarks></remarks>

  void Load(Stream s);

  /// <summary>
  /// Your plugin must setup a GUID that is unique for your project. The Main Program will check this and if it is duplicated your DLL will not load
  /// </summary>
  /// <value></value>
  /// <returns></returns>
  /// <remarks></remarks>

  Guid Identifier { get; }
  /// <summary>
  /// This Description will be displayed for the user to select your Plugin. You should make this descriptive so the correct one is selected in the event they have multiple plugins active.
  /// </summary>
  /// <value></value>
  /// <returns></returns>
  /// <remarks></remarks>

  string Description { get; }

}

Now in your Main Project add a Reference to the above DLL.

In your main project scan a directory for the DLL's you are planning to load (c:\myDlls*.dll) use a DirectoryInfo (or similar) to scan.

Once you have found a DLL, use the Assembly asm = Assembly.LoadFrom(filename) to get it loaded.

Now you can do this in the main project.

foreach (Type t in asm.GetTypes())
{
    if (typeof(ISettings).IsAssignableFrom(t)) 
    {
        //Found a Class that is usable
        ISettings loadedSetting = Activator.CreateInstance(t);
        Console.WriteLine(loadedSetting.Description);
    }  
}


标签: c# .net dll