This article series is intended to provide an introduction to Microsoft's Managed Extensibility Framework. Before showing the code snippets using the MEF framework, I would like to run through a case scenario where the need for MEF can be felt. I feel this will help in identifying the situations where MEF can be used effectively.
Building reusable code into libraries and using them in applications is normal. In legacy Win32 applications, if a library is linked to an application, a linker is used to add a reference to that library in the PE header. When the runtime tries to load this application, a loader is used to perform the following operations:
But in .NET world, with CLR and JIT compilation in the middle, things are slightly different. Now, the loader tries to load
those libraries which are needed to execute just the Main method. As other methods are called from Main, the IL for those methods
are JIT compiled on demand and loaded into memory. These aspects form the basic core for MEF.
Here is how it is defined officially:
"The Managed Extensibility Framework (MEF) is a new library in .NET that enables greater reuse of applications and components. Using MEF, .NET applications can make the shift from being statically compiled to dynamically composed. If you are building extensible applications, extensible frameworks and application extensions, then MEF is for you."
Take the middle part: "statically compiled to dynamically composed". It is all about that.
I would like to formulate a case where the need for MEF can be felt. Let us create a console application for this purpose.
namespace WOMEF
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Enter 0 to quit, " +
"any other number to continue");
while (Console.ReadLine() != "0")
{
new Program().SayHelloToUser();
}
}
public void SayHelloToUser()
{
Console.WriteLine("In SayHelloToUser");
IWOMEFLib womeflib = new CWOMEFLib();
womeflib.SayHello(Environment.UserDomainName +
Environment.UserName);
Console.ReadLine();
}
}
}
Let us define IWOMEFLib and CWOMEFLib in a library:
using System;
namespace WOMEFLib
{
public interface IWOMEFLib
{
int SayHello(string username);
}
public class CWOMEFLib : IWOMEFLib
{
#region IWOMEFLib Members
public int SayHello(string username)
{
Console.WriteLine("\"" + "Say Hello to " +
username + "\"");
return 0;
}
#endregion
}
}
Now, in the WOMEF project, add a project reference to the WOMEFLib project. Change the configuration to Release, build, and test the application. Nothing great about this. But, we are going to witness some interesting facts with these two assemblies.
The points that will be verified are:
SayHelloToUser method is JIT compiledIn order to check these scenarios, we are going to view the method table and descriptors of the application WOMEFLib.exe in runtime.
We are going to use Windbg here. If you don't have it installed, download it here.
Here, we are going to check when the loader loads WOMEFLib.dll.
ntdll!DbgBreakPoint:
7c90120e cc int 3
ntdll!DbgBreakPoint:
7c90120e cc int 3
Missing image name, possible paged-out or corrupt data.
0:003> .loadby sos.dll mscorwks
0:003> !dumpdomain
*** ERROR: Symbol file could not be found.
Defaulted to export symbols for C:\WINDOWS\Microsoft.NET\... -
PDB symbol for mscorwks.dll not loaded
--------------------------------------
System Domain: 7a3bd058
LowFrequencyHeap: 7a3bd07c
HighFrequencyHeap: 7a3bd0c8
StubHeap: 7a3bd114
Stage: OPEN
Name: None
--------------------------------------
Shared Domain: 7a3bc9a8
LowFrequencyHeap: 7a3bc9cc
HighFrequencyHeap: 7a3bca18
StubHeap: 7a3bca64
Stage: OPEN
Name: None
Assembly: 001b0628
--------------------------------------
Domain 1: 0016d298
LowFrequencyHeap: 0016d2bc
HighFrequencyHeap: 0016d308
StubHeap: 0016d354
Stage: OPEN
SecurityDescriptor: 0016e5c0
Name: WOMEF.exe
Assembly: 001b0628 [C:\WINDOWS\assembly\GAC_32\mscorlib\
2.0.0.0__b77a5c561934e089\mscorlib.dll]
ClassLoader: 001b06a8
SecurityDescriptor: 001ae190
Module Name
033e1000 C:\WINDOWS\assembly\GAC_32\mscorlib\
2.0.0.0__b77a5c561934e089\mscorlib.dll
Assembly: 001b9800 [D:\My Documents\Visual Studio 2008\
Projects\MEF\WOMEF\WOMEF\bin\Release\WOMEF.exe]
ClassLoader: 001b9e68
SecurityDescriptor: 001b96c8
Module Name
00a72c5c D:\My Documents\Visual Studio 2008\Projects\MEF\
WOMEF\WOMEF\bin\Release\WOMEF.exe
Assembly: 001bcb30 [D:\My Documents\Visual Studio 2008\
Projects\MEF\WOMEF\WOMEF\bin\Release\WOMEFLib.dll]
ClassLoader: 001bbc98
SecurityDescriptor: 001bcd28
Module Name
00a730c8 D:\My Documents\Visual Studio 2008\Projects\MEF\
WOMEF\WOMEF\bin\Release\WOMEFLib.dll
Main method.
Also, we don't use any type from WOMEF.dll in the Main method. The loader loaded this assembly because it is there in the manifest.
Now, let us see what happens if this assembly is not available when we start WOMEF.exe. Kill the application by pressing Control+C. If needed, close Windbg and reopen.Here, we are going to check what happens if WOMEFLib.dll is missing when the application is starting. Move WOMEFLib.dll from the Release folder to some other folder outside the solution directory (say to the Desktop; don't copy cut it).
ntdll!DbgBreakPoint:
7c90120e cc int 3
SayHelloToUser is not yet JIT compiled.
And, we will verify this fact in the next section. Kill the application by pressing Control+C. If needed, close Windbg and reopen.Here, we are going to check when the SayHelloToUser method is actually JIT compiled. Move WOMEFLib.dll back to the Release folder.
ntdll!DbgBreakPoint:
7c90120e cc int 3
ntdll!DbgBreakPoint:
7c90120e cc int 3
Missing image name, possible paged-out or corrupt data.
0:003> .loadby sos.dll mscorwks
0:003> !dumpdomain
*** ERROR: Symbol file could not be found.
Defaulted to export symbols for C:\WINDOWS\Microsoft.NET\... -
PDB symbol for mscorwks.dll not loaded
--------------------------------------
System Domain: 7a3bd058
LowFrequencyHeap: 7a3bd07c
HighFrequencyHeap: 7a3bd0c8
StubHeap: 7a3bd114
Stage: OPEN
Name: None
--------------------------------------
Shared Domain: 7a3bc9a8
LowFrequencyHeap: 7a3bc9cc
HighFrequencyHeap: 7a3bca18
StubHeap: 7a3bca64
Stage: OPEN
Name: None
Assembly: 001b0628
--------------------------------------
Domain 1: 0016d298
LowFrequencyHeap: 0016d2bc
HighFrequencyHeap: 0016d308
StubHeap: 0016d354
Stage: OPEN
SecurityDescriptor: 0016e5c0
Name: WOMEF.exe
Assembly: 001b0628 [C:\WINDOWS\assembly\GAC_32\mscorlib\
2.0.0.0__b77a5c561934e089\mscorlib.dll]
ClassLoader: 001b06a8
SecurityDescriptor: 001ae190
Module Name
033e1000 C:\WINDOWS\assembly\GAC_32\mscorlib\
2.0.0.0__b77a5c561934e089\mscorlib.dll
Assembly: 001b9800 [D:\My Documents\Visual Studio 2008\
Projects\MEF\WOMEF\WOMEF\bin\Release\WOMEF.exe]
ClassLoader: 001b9e68
SecurityDescriptor: 001b96c8
Module Name
"00a72c5c" D:\My Documents\Visual Studio 2008\Projects\
MEF\WOMEF\WOMEF\bin\Release\WOMEF.exe
Assembly: 001bcb30 [D:\My Documents\Visual Studio 2008\
Projects\MEF\WOMEF\WOMEF\bin\Release\WOMEFLib.dll]
ClassLoader: 001bbc98
SecurityDescriptor: 001bcd28
Module Name
00a730c8 D:\My Documents\Visual Studio 2008\Projects\MEF\
WOMEF\WOMEF\bin\Release\WOMEFLib.dll
Main method. Let us get the address of the method table of the assembly WOMEF.exe by using the command: !dumpmodule
-mt <module address 00a72c5c>. You can see the module address is in red color.0:003> !dumpmodule -mt 00a72c5c
Name: D:\My Documents\Visual Studio 2008\Projects\MEF\
WOMEF\WOMEF\bin\Release\WOMEF.exe
Attributes: PEFile
Assembly: 001b9780
LoaderHeap: 00000000
TypeDefToMethodTableMap: 00a700c0
TypeRefToMethodTableMap: 00a700cc
MethodDefToDescMap: 00a70128
FieldDefToDescMap: 00a70138
MemberRefToDescMap: 00a7013c
FileReferencesMap: 00a701a4
AssemblyReferencesMap: 00a701a8
MetaData start address: 004020c0 (1864 bytes)
Types defined in this module
MT TypeDef Name
------------------------------------------------------------
"00a73010" 0x02000002 WOMEF.Program
Types referenced in this module
MT TypeRef Name
------------------------------------------------------------
03650508 0x01000001 System.Object
03654258 0x01000012 System.Console
036508ec 0x01000013 System.String
00a73484 0x01000016 WOMEFLib.IWOMEFLib
0:003> !dumpmt -md 00a73010
EEClass: 00a712f4
Module: 00a72c5c
Name: WOMEF.Program
mdToken: 02000002 (D:\My Documents\Visual Studio 2008\
Projects\MEF\WOMEF\WOMEF\bin\Release\WOMEF.exe)
BaseSize: 0xc
ComponentSize: 0x0
Number of IFaces in IFaceMap: 0
Slots in VTable: 7
--------------------------------------
MethodDesc Table
Entry MethodDesc JIT Name
035a6a70 03424934 PreJIT System.Object.ToString()
035a6a90 0342493c PreJIT System.Object.Equals(System.Object)
035a6b00 0342496c PreJIT System.Object.GetHashCode()
036172f0 03424990 PreJIT System.Object.Finalize()
00a7c019 00a73008 NONE WOMEF.Program..ctor()
00de0070 00a72ff0 JIT WOMEF.Program.Main(System.String[])
00a7c015 00a72ffc "NONE" WOMEF.Program.SayHelloToUser()
SayHelloToUser method as NONE.
This means that SayHelloToUser is not yet JIT compiled. Let us run the application
by pressing F5 in Windbg and entering a non-zero value in the console. As soon as you enter a non-zero value in the console, the CLR compiles
SayHelloUser, and loads it with a reference to the SayHello method in WOMEFLib.dll. Now, the console waits for the input because of ReadLine.0:003> !dumpmt -md 00a73010
EEClass: 00a712f4
Module: 00a72c5c
Name: WOMEF.Program
mdToken: 02000002 (D:\My Documents\Visual Studio 2008\Projects\
MEF\WOMEF\WOMEF\bin\Release\WOMEF.exe)
BaseSize: 0xc
ComponentSize: 0x0
Number of IFaces in IFaceMap: 0
Slots in VTable: 7
--------------------------------------
MethodDesc Table
Entry MethodDesc JIT Name
035a6a70 03424934 PreJIT System.Object.ToString()
035a6a90 0342493c PreJIT System.Object.Equals(System.Object)
035a6b00 0342496c PreJIT System.Object.GetHashCode()
036172f0 03424990 PreJIT System.Object.Finalize()
00a7c019 00a73008 NONE WOMEF.Program..ctor()
00de0070 00a72ff0 JIT WOMEF.Program.Main(System.String[])
00de00d0 00a72ffc "JIT" WOMEF.Program.SayHelloToUser()
SayHelloToUser method as JIT. This makes it clear that the SayHelloToUser method would have never been JIT compiled
if a zero is entered in the console window as the first option. So, when the control goes inside the while loop, the CLR started JIT compiling the called method.Here, we are going to check what happens if WOMEFLib.dll is copied to the application directory while it is waiting for user input for the first time. Move WOMEFLib.dll from the Release folder to some other folder outside the solution directory (say to Desktop; don't copy cut it).
SayHelloToUser, it tries to refer
the type CWOMEF in WOMEFLib.dll and fails to resolve the assembly. It comes out with an exception:Unhandled Exception: System.IO.FileNotFoundException:
Could not load file or assembly 'WOMEFLib, ..
OK, with that background, I hope you are clear on what MEF is trying to solve. But before looking at MEF, let us see how we can solve this particular problem with existing technologies. With CLR 2.0, we have an option of loading the assembly at run time. After loading the assembly at run time, we can create objects of the types defined in it and we can even invoke them using Reflection techniques. This is quite straightforward. I will just provide a working sample. Do the following changes to our solution.
In the WOMEF project, remove the reference to WOMEFLib.dll. Then, replace the code in Program.cs with the following snippet:
using System;
using System.Reflection;
namespace WOMEF
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Enter 0 to quit, any other number to continue");
while (Console.ReadLine() != "0")
{
new Program().SayHelloToUser();
}
}
public void SayHelloToUser()
{
Console.WriteLine("In SayHelloToUser");
InvokeMethod("WOMEFLib", "CWOMEFLib", "SayHello", new object[] {
Environment.UserDomainName+Environment.UserName });
Console.ReadLine();
}
public object InvokeMethod(string assemblyName, string className,
string methodName, object[] parameters)
{
System.Type[] parametersTypes;
int parametersCount = 0;
object returnObject = null;
try
{
Type type = System.Reflection.Assembly.LoadFrom(assemblyName +
".dll").GetType(assemblyName + "." + className);
parametersTypes = new System.Type[parameters.GetUpperBound(0) + 1];
foreach (object parameter in parameters)
{
if (parameter != null)
parametersTypes[parametersCount] = parameter.GetType();
parametersCount++;
}
MethodInfo mi = type.GetMethod(methodName, parametersTypes);
ConstructorInfo ci = type.GetConstructor(Type.EmptyTypes);
object objectinstance = ci.Invoke(null);
//Invoke
returnObject = mi.Invoke(objectinstance, parameters);
}
catch (TargetException tex)
{
throw tex;
}
return returnObject;
}
}
}
Here, the Invoke method loads the assembly (WOMEFLib) at run time, constructs the class object (CWOMEFLib), and invokes a method (SayHello) in it.
In my next post, we will see how MEF not only simplifies this process but also provides many more useful options for this kind of scenarios.
-
| You must Sign In to use this message board. | ||||||
|
||||||
|
||||||
|
||||||