Page 1 of 1

Using reflection to load unreferenced assemblies at runtime

#1 jacobjordan  Icon User is offline

  • class Me : Perfection
  • member icon

Reputation: 113
  • View blog
  • Posts: 1,499
  • Joined: 11-June 08

Posted 01 January 2009 - 04:13 PM

Sure, we can define our application's referenced assemblies easily, but how would one interact with types in assemblies at runtime that we don't directly reference? This can be quite a problem for someone who is creating plugin or addon support for their application, where the assemblies they must reference will be added post-build. One of the functions of the System.Reflection namespace is loading assemblies and accessing their contained types.

Wikipedia said:

System.Reflection
Provides an object view of types, methods, and fields. You have "the ability to dynamically create and invoke types". It exposes the API to access the Reflective programming capabilities of CLR.


Reflecting on an assembly and its types

To demonstrate this, let's setup a hypothetical situation in which we have an application, and a .NET DLL unreferenced by our application whose types we need to access at runtime. The DLL will be called MyDLL.dll, and the application will be called MyApp.exe. How would we view the types contained inside MyDLL from MyApp at runtime when MyDLL isn't referenced by MyApp? The System.Runtime.Assembly class can be used to access MyDLL in the following manner
System.Reflection.Assembly myDllAssembly = System.Reflection.Assembly.LoadFile("%MyDLLPath%\\MyDLL.dll");


The myDllAssembly object can now be used to access the types contained inside MyDLL. To access a type contained inside MyDLL, System.Reflection.Assembly possesses a method called GetType() which can return a System.Type that can be used to access all the members of the type we want to retrieve from inside MyDLL. Note that the System.Reflection.Assembly.GetType() method is not to be confused with the GetType() method possessed by all objects; they are related, but quite different and we will discuss the other GetType() method later. Back to our example, let's say there is a type inside MyDLL called MyDLLForm which inherits from System.Windows.Forms.Form and is contained in the namespace MyDLLNamespace. To obtain the System.Type of this, we could do the following
System.Reflection.Assembly myDllAssembly = System.Reflection.Assembly.LoadFile("%MyDLLPath%\\MyDLL.dll");
System.Type MyDLLFormType = myDllAssembly.GetType("MyDLLNamespace.MyDLLForm");


MyDLLFormType now holds the System.Type of MyDLLForm. We can now access all the members and contained types inside MyDLLForm from MyDLLFormType. However, using this GetType() method will not create an instance of MyDLLForm. To access a type contained inside MyDLL and create an instance of it, System.Reflection.Assembly possesses another method called CreateInstance(). Say we wanted to create an instance of MyDLLForm, we could use something like this
System.Reflection.Assembly myDllAssembly = System.Reflection.Assembly.LoadFile("%MyDLLPath%\\MyDLL.dll");
Form MyDLLFormInstance = (Form)myDllAssembly.CreateInstance("MyDLLNamespace.MyDLLForm");


MyDLLFormInstance now holds an instance of MyDLLForm (remember that MyDLLForm inherits from Form). To create an instance of MyDLLForm, that method will invoke MyDLLForm's constructor. By way of that overload, CreateInstance is expecting a constructor that takes no arguments, as we only passed it the type name we want to create an instance of. However, lets say there is a constructor we want to use to create an instance of MyDLLForm that takes two arguments, a string and an int. To do that, and for example to pass it a string of "Hi" and an integer value of 113 (one of my favorite numbers), we could use this
System.Reflection.Assembly myDllAssembly = System.Reflection.Assembly.LoadFile("%MyDLLPath%\\MyDLL.dll");

//the arguments we will pass
object[] argstopass = new object[] { (object)"Hi", (object)113 };

Form MyDLLFormInstance = (Form)myDllAssembly.CreateInstance("MyDLLNamespace.MyDLLForm",
     false, //do not ignore the case
     BindingFlags.CreateInstance, //specifies we want to call a ctor method
     null, //a null binder specifies the default binder will be used (works in most cases)
     argstopass, //the arguments (based on the arguments, CreateInstance() will decide which ctor to invoke)
     null, //CultureInfo is null so we will use the culture info from the current thread
     null //no activation attributes
     );


Read the comments, they should tell you what's happening pretty good.

Reflecting on a single type


Fields

The CreateInstance() method can easily create an instanced object of any type in an assembly we've loaded in a System.Reflection.Assembly, however, you can see that in the example above MyDLLFormInstance is of type System.Windows.Forms.Form, which is the inherited type of MyDLLForm. However, say there are custom members contained inside MyDLLForm that we need to access. MyDLLFormInstance is of type System.Windows.Forms.Form, so any members we have created inside MyDLLForm we cannot access directly by MyDLLFormInstance. This is where that other GetType() methods that all objects possess comes in handy. Lets say for an example, there is a public string variable contained inside MyDLLForm called StringVariable. To retrieve the value of that variable from MyDLLFormInstance, the following code can be used
string StringVariableValue = (String)MyDLLFormInstance
    .GetType() //Get the type of MyDLLForm
    .GetField("StringVariable") //Get a System.Reflection.FieldInfo object representing StringVariable
    .GetValue(MyDLLFormInstance); //Get the actual value of StringVariable


That will assign StringVariableValue to the value of StringValue contained in MyDLLFormInstance. The GetType() method is used to get the System.Type of MyDLLFormInstance. It will return a type that represents MyDLLForm, because even though MyDLLFormInstance is declared as a Form, it's actual value is of MyDLLForm. We can then use the GetField() method of that System.Type to return the value of the StringVariable field in MyDLLFormInstance.

Say, however, we want to set StringVariable in that same instance instead of retrieving its value. We can use almost the same method, except instead of calling the GetValue() method of the FieldInfo that represents StringValue, we can use the SetValue() method like so
MyDLLFormInstance
    .GetType() //Get the type of MyDLLForm
    .GetField("StringVariable") //Get a System.Reflection.FieldInfo object representing StringVariable
    .SetValue(MyDLLFormInstance, //the object whose StringVariable field we want to set (MyDLLFormInstance)
        (object)"This string will be the new value of StringVariable" //the new value of StringVariable
        );


That will then set the StringVariable field in MyDLLFormInstance.

Properties

But what if there is a property in MyDLLForm that we want to retrieve in MyDLLFormInstance, called StringProperty, for example. We can use this method to get the property's value
MyDLLFormInstance
    .GetType() //Get the type of MyDLLForm
    .GetProperty("StringProperty") //Gets a System.Reflection.PropertyInfo object representing StringProperty
    .GetValue(MyDLLFormInstance, null); //Gets the property's value


Now, if we wanted to set the value of the same property (if the property isn't read-only)
MyDLLFormInstance
    .GetType() //Get the type of MyDLLForm
    .GetProperty("StringProperty") //Gets a System.Reflection.PropertyInfo object representing StringProperty
    .SetValue(MyDLLFormInstance, (object)"This will be the new value of StringProperty", null); //Sets the value of StringProperty



Methods

Now you know how to modify and retrieve the value of public fields and properties contained inside assemblies loaded in an System.Reflection.Assembly object. However, probably the most important is knowing how to invoke methods. So, using the same MyDLL scenario, lets say there is a method in MyDLLForm that returns void and takes no arguments called SomeMethod. This will be quite easy to invoke, as there is no arguments to pass, and we will not need to retrieve the value. To invoke that method, we can easily use, on the same MyDLLFormInstance object
MyDLLFormInstance
    .GetType() //Get the type of MyDLLForm
    .GetMethod("SomeMethod") //Gets a System.Reflection.MethodInfo object representing SomeMethod
    .Invoke(MyDLLFormInstance, null); //here we invoke SomeMethod. The arguments list is null because it takes no arguments


Lets make this situation a bit more complex. Lets say SomeMethod takes 2 arguments, a string and an int. Lets use the same two example values from our CreateInstance example; "Hi" as the string and 113 and the integer. Remember, SomeMethod takes arguments, but it still returns void, so we don't need to retrieve the value. To invoke SomeMethod this time, we could use
//This is the arguments list. "Hi" is the string and 113 is the int
object[] argstopass = new object[] { (object)"Hi", (object)113 };

MyDLLFormInstance
    .GetType() //Get the type of MyDLLForm
    .GetMethod("SomeMethod") //Gets a System.Reflection.MethodInfo object representing SomeMethod
    .Invoke(MyDLLFormInstance, argstopass); //here we invoke SimpleMethod. We also pass ArgsToPass as the argument list


Now, let's make this as complicated as we can get it. Lets say that SomeMethod takes those same arguments, and returns a value of string which you need to retrieve when you invoke SomeMethod. This is really quite easy, because the Invoke() method we used in the past two examples returns a type of object, which will hold the value returned by the method it invoked. So, to get the string returned by SomeMethod, we can use
//This is the arguments list. "Hi" is the string and 113 is the int
object[] argstopass = new object[] { (object)"Hi", (object)113 };

string ReturnValue = (string)MyDLLFormInstance
    .GetType() //Get the type of MyDLLForm
    .GetMethod("SomeMethod") //Gets a System.Reflection.MethodInfo object representing Some
    .Invoke(MyDLLFormInstance, argstopass) //here we invoke SomeMethod. We also pass ArgsToPass as the argument list


In that example, ReturnValue will hold the value returned by SomeMethod when we invoked it.

Events

Now, what about events? Lets say we have an event in MyDLLForm called SomeEvent that we want to access in MyDLLFormInstance. To obtain a System.Reflection.EventInfo object representing that event, simply use
MyDLLFormInstance
    .GetType() //Get the type of MyDLLForm
    .GetEvent("SomeEvent"); //Gets a System.Reflection.EventInfo object representing SomeEvent


Now, lets pretend that event uses a simple EventHandler delegate. To add an event handler for that event, use
MyDLLFormInstance
    .GetType() //Get the type of MyDLLForm
    .GetEvent("SomeEvent") //Gets a System.Reflection.EventInfo object representing SomeEvent
    .AddEventHandler(MyDLLFormInstance, new EventHandler(SomeEventHandler)); //Adds SomeEventHandler as an event handler for SomeEvent

//------------------------------------------------------------------------------------------

//This method will be the event handler for SomeEvent in MyDLLFormInstance
public void SomeEventHandler(object sender, EventArgs e)
{

}


And to remove that same event handler
MyDLLFormInstance
    .GetType()
    .GetEvent("SomeEvent") //Gets a System.Reflection.EventInfo object representing SomeEvent
    .RemoveEventHandler(MyDLLFormInstance, new EventHandler(SomeEventHandler)); //Removes SomeEventHandler as an event handler for SomeEvent

//------------------------------------------------------------------------------------------

//This method will be removed as the event handler for SomeEvent in MyDLLFormInstance
public void SomeEventHandler(object sender, EventArgs e)
{


}



Nested Types

What if there are types contained inside a type in an assembly? That is quite easy. Going along with our example, let's say MyDLLForm contains a nested class called MyDLLFormNestedType. To access that, use
myDllAssembly
    .GetType("MyDLLForm") //Gets a type representing MyDLLForm
    .GetNestedType("MyDLLFormNestedType") //Gets a type representing MyDLLFormNestedType, which is nested inside MyDLLForm


Notice we are not accessing MyDLLFormNestedType through MyDLLFormInstance, but instead we are accessing the assembly with MyDllAssembly, and then retrieving the System.Type of MyDLLForm, and from that retrieving the System.Type of the nested type MyDLLFormNestedType. You can then interact with that nested type the same as you can interact with the type containing it (creating instances of it, accessing the members contained in it, etc).

Though this example we used throughout this tutorial was pertaining to loading a class inheriting from form contained inside a DLL, don't for a second think that i'm implying that you can only access classes contained inside an assembly. You can access any type, class, struct, interface, or anything. Not only can System.Reflection be used to load unreferenced assemblies, as demonstrated in this tutorial, but it can be used to make powerful decompilers and code analyzers. .NET Reflector, a powerful .NET decompiler, would not exist if it wasn't for the System.Reflection namespace and the reflective capabilities of the CLR.

Is This A Good Question/Topic? 1
  • +

Replies To: Using reflection to load unreferenced assemblies at runtime

#2 abhilash.katiyar  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 1
  • Joined: 23-December 08

Posted 06 January 2009 - 05:55 AM

Good Job !!
Was This Post Helpful? 0
  • +
  • -

#3 dom96  Icon User is offline

  • D.I.C Regular

Reputation: 4
  • View blog
  • Posts: 256
  • Joined: 29-December 08

Posted 08 January 2009 - 01:24 PM

Awesome tutorial
Was This Post Helpful? 0
  • +
  • -

#4 Guest_emmanuel*


Reputation:

Posted 09 December 2010 - 03:48 PM

i had similar needs to create instance to Dlls on the fly, my question here, is if i later need this assembly to be replaced by another , i notice that i had now 2 dlls running, how could i destroy the first instance? this DLL needs to be running all teh time reading a XML file, so i was fin till i found that i need to replace it, and i found out both are running now, here is how i am creating the reference.

namespace MainSyncService.Repository
{
public class AssemblyRepository<T> : IRepository
{
#region declarations

private Assembly _AssemblyContext;
private Type _type;
public Object _ClassObject;

#endregion


#region Dinamically
public static Hashtable AssemblyReferences = new Hashtable();
public static Hashtable ClassReferences = new Hashtable();

#region Constructors
public AssemblyRepository()
{

}

public AssemblyRepository(Type t, Object c)
{
_type = t;
_ClassObject = c;
}

#endregion

/// <summary>
///
/// </summary>
/// <param name="_ass"></param>
/// <returns>an AssemblyInstanceReturned entitie, contaning the object returned from the method of the assembly
/// and the hastables created in case we might want to clean them later</returns>
public AssemblyInstanceReturned CreateAssembly(AssemblyE _ass)
{
AssemblyInstanceReturned _assemblyreturned = new AssemblyInstanceReturned();
object instace = new object();
try
{
AssemblyRepository<AssemblyE> ci = GetClassReference(_ass.AssemblyName, _ass.ClassName);
instace= InvokeMethod(ci, _ass.MethodName, _ass.Args);

List<Hashtable> _listofHashTables = new List<Hashtable>();
_listofHashTables.Add(AssemblyReferences);
_listofHashTables.Add(ClassReferences);

_assemblyreturned.ObjectReturned = instace;
_assemblyreturned.ListofHashTables = _listofHashTables;
_assemblyreturned.ObjectInstanceAssembly = ci;

#region addinfo Log

//Add information to log
EventWriter.Log(new EventLogBitmore.Event()
{
Application = "MainSyncService.Repository.AssemblyRepository",
Eventt = "CreateAssembly()",
Date = DateTime.Now,
CustomMessage = "Creating the assembly references Success",
TypeEvent = EventLogBitmore.EventType.Information,
SystemErrorr = "",
SystemMessage = "",
FileName = "AssemblyRepository.cs",
IdStatusDistribution = 1

});
#endregion

}
catch (Exception me)
{
#region adderrorLog
//add error to log
EventWriter.Log(new EventLogBitmore.Event()
{
Application = "MainSyncService.Repository.AssemblyRepository",
Eventt = "CreateAssembly()",
Date = DateTime.Now,
CustomMessage =new AssemblyInstanceException("Error Creating the assembly references Failed").ToString(),
TypeEvent = EventLogBitmore.EventType.Error,
SystemErrorr = me.Source.ToString(),
SystemMessage = string.Concat(me.Message.ToString(), "||", me.StackTrace.ToString()),
FileName = "AssemblyRepository.cs",
IdStatusDistribution = 3
});
#endregion
}

return _assemblyreturned;
}
#endregion


#region Interface

public AssemblyRepository<AssemblyE> GetClassReference(string AssemblyName, string ClassName)
{

if (ClassReferences.ContainsKey(AssemblyName) == false)
{
Assembly assembly;
if (AssemblyReferences.ContainsKey(AssemblyName) == false)
{
AssemblyReferences.Add(AssemblyName,
assembly = Assembly.LoadFrom(AssemblyName));
}
else
assembly = (Assembly)AssemblyReferences[AssemblyName];

// Walk through each type in the assembly

foreach (Type type in assembly.GetTypes())
{
if (type.IsClass == true)
{
// doing it this way means that you don't have

// to specify the full namespace and class (just the class)

if (type.FullName.EndsWith("." + ClassName))
{
AssemblyRepository<AssemblyE> ci = new AssemblyRepository<AssemblyE>(type,
Activator.CreateInstance(type));
ClassReferences.Add(AssemblyName, ci);
return (ci);
}
}
}
throw (new System.Exception("could not instantiate class"));
}
return ((AssemblyRepository<AssemblyE>)ClassReferences[AssemblyName]);
}

public object InvokeMethod(AssemblyRepository<AssemblyE> ci, string MethodName, object[] args)
{
// Dynamically Invoke the method

Object Result = ci._type.InvokeMember(MethodName,
BindingFlags.Default | BindingFlags.InvokeMethod,
null,
ci._ClassObject,
args);
return (Result);
}


#endregion

}
}


i call it in this way

AssemblyE _ass = new AssemblyE(DepositManagerMemory.AssemblyName, DepositManagerMemory.AssemblyClassName, DepositManagerMemory.AssemblyMethodName, DepositManagerMemory.Args);
ImplementService CCU = new ImplementService(_ass);
_assemblyreturnedMemory = CCU.mainMethod();



ass you can see first i am creating a new object with the constructor and later i call the main method
what i got back its from instance the assembly its an object i had created with some information i though was enough for killing the assembly later

this is the object i will paste just this part because i fell it is only this necesary for you in order to have a better understanding.

public class AssemblyInstanceReturned
{
#region Private fields
private Object _objectReturned;
private Object _objectInstanceAssembly;
private List<Hashtable> _listofHashTables;
...properties here
...constructors here
}

how can i Kill this assembly? after i had created?
Was This Post Helpful? 0

#5 kepp  Icon User is offline

  • New D.I.C Head

Reputation: 8
  • View blog
  • Posts: 35
  • Joined: 18-November 10

Posted 14 December 2010 - 11:31 AM

For those writing plugin and adons, custom attributes can be used. you can access these just as easily with
System.Reflection. Loading a reference from a directory where there might not just be the dll's for the main application cannot be good? or am i wrong?
I mean, it might just contain any dll in the world as far as you know! Trust the name of the dll?
Or trust the source within it?
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1