0

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.

ThinkingStiff
  • 64,767
  • 30
  • 146
  • 239
John
  • 1,095
  • 3
  • 15
  • 31
  • Post the code that you have..? Reflection is a way of getting at assemblies Methods, names, types ect.. read up on Reflection it's quite simple once you gain an understanding on how LateBinding is done is a Managed environment vs unManaged back in the legacy code days – MethodMan Jan 04 '12 at 21:59
  • do you know what methods in the dll you need to call? (I'm guessing you are using unmanaged) – Adrian Jan 04 '12 at 21:59
  • Is it managed or unmanaged DLL? – Krizz Jan 04 '12 at 21:59
  • You'll need to use `Assembly.LoadFrom()` to dynamically load your assembly at runtime, which will give you an `Assembly` variable which you can inspect. – M.Babcock Jan 04 '12 at 22:02
  • Use ConstructorInfo.Invoke() to create the class object. Assembly.CreateInstance() is a handy shortcut. – Hans Passant Jan 04 '12 at 22:03
  • If it's a managed .Net DLL, why can't you just add a reference to it to your project and include the namespace in the source files that need to create the objects from it? If you don't know the namespaces in the DLL, object explorer will display them. – Jeff LaFay Jan 05 '12 at 14:07

4 Answers4

4

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.

Gayot Fow
  • 8,710
  • 1
  • 35
  • 48
Tigran
  • 61,654
  • 8
  • 86
  • 123
  • I got this far but how to go on from here? What can I do with 'o'? – John Jan 04 '12 at 22:05
  • @John: see my edited post. You just need to cast to a type you already know. that is. – Tigran Jan 04 '12 at 22:07
  • how can I do that when 'namespace.classname' is not know @ compile time? VS gives the error 'the type or namespace name 'namespace' could not be found' PS its managed – John Jan 04 '12 at 22:12
  • @John: you *can not* load a type that you don't know the name of. If you don't know the name of the type instance of which you gonna construct, that means that the question has no sence and you're doing something wrong. – Tigran Jan 04 '12 at 22:13
  • I dont get it. The dll comes from outerspace and can be a hundred different things. However it has 1 type I know which I want to interact with. However I dont have that type yet at compile time. How can the IDE know about it? – John Jan 04 '12 at 22:19
  • @John: so, intead of `namespace.classname` write **your known class** `namespace`.`classname`. That is. – Tigran Jan 04 '12 at 22:22
  • How can I know it when the DLL has yet to arrive from somewhere? I'm starting to think I either stupid or asking the wrong questions – John Jan 04 '12 at 22:26
  • I cant chat as of my reputation it seems. – John Jan 04 '12 at 22:32
  • @John: what is the name of the `type` you're talking about? I mean the *fully qualified name*, so `namespace.classname`?? – Tigran Jan 04 '12 at 22:33
  • Atm it is Test.ExternalDllTest – John Jan 04 '12 at 22:35
  • @John: I edited my answer with you type name, and `dynamic` type use for easy calling you type's method. **This valid only from 4.0 Framework version**. `SomeMethod()` provided is any method of your `ExternalDLLTest` class you may want to call. – Tigran Jan 04 '12 at 22:41
  • Thanks I got it working now. However I dont understand what dynamic actually does. Could you please elaborate? – John Jan 04 '12 at 23:29
  • `dynamic` figures out the type at **run time** and using your method name like a pointer finds it and calls it. Search in internet fro more detailed explanation. – Tigran Jan 04 '12 at 23:42
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.

ChrisWue
  • 18,612
  • 4
  • 58
  • 83
0

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);
    }  
}
Paul Farry
  • 4,730
  • 2
  • 35
  • 61
  • Could you give me an example on how to implement this please? One for the loader and one the external DLL. – John Jan 04 '12 at 22:43
  • This seems a great way of working, Thanks. What is a 'guid'? What is the function of it? – John Jan 04 '12 at 23:32
  • Guid is a pretty standard part of windows. This is just an example I pulled from a project I've been working on. You just place whatever properties/methods you require in your interface. – Paul Farry Jan 04 '12 at 23:41
0

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...

Gayot Fow
  • 8,710
  • 1
  • 35
  • 48