Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Mobile Development – Compact Framework: Managed Extension Framework (MEF)

0.00/5 (No votes)
17 Feb 2012CPOL4 min read 19.2K  
Mobile Development – Compact Framework: Managed Extension Framework (MEF)

This post describes a way to load class libraries (DLLs) dynamically on demand in your compact framework application.

What Is It Good For?

Hardware Abstraction. Load Only Matching Libraries

Organize your development for different devices. You will be able to only load the right hardware (device) dependent libraries. You are able to use a hardware abstraction layer.

Normally, you add a reference to an assembly during development in Visual Studio. Now, when the application starts on the device, it will try to load this assembly. If it contains hardware dependent code, like a barcode scanner library, the load of your application may fail.

Using the techniques described here, you can avoid this and direct your app to only load device matching libraries.

plug in Usage

You can write a Compact Framework application that will load and work with plugins. These reside in DLLs and can be added or removed on demand.

You can, for example, write an application launcher that will list the installed extensions. In example for a Transport and Logistic application, you can have a truck loading and a truck unloading extension. If you have to update one of the plugins, you only need to replace the plug in DLL, the rest of the application (for example Login forms, Information screens, connection management) remains unchanged.

DLL Loading in C/C++

Loading libs dynamically is well known in C/C++. You simply use LoadLibrary(dllName) and then you can get the addresses to known library functions and use these.

Static linking: Adding a reference in a compact framework (SmartDevice) application inside Visual Studio is like adding a header and lib file to a C/C++ application.

DLL Loading in Compact Framework

You can also use the above in C#/VB.NET.

C#
namespace ConsoleApplication1
{
  class Program
  {
    static IClass1 GetIClass1(string filename)
    {
      Assembly classLibrary1 = null;
      using (FileStream fs = File.Open(filename, FileMode.Open))
      {
        using (MemoryStream ms = new MemoryStream())
        {
          byte[] buffer = new byte[1024];
          int read = 0;
          while ((read = fs.Read(buffer, 0, 1024))>0)
            ms.Write(buffer, 0, read);
          classLibrary1 = Assembly.Load(ms.ToArray());
        }
      }
      foreach (Type type in classLibrary1.GetExportedTypes())
      {
        if (type.GetInterface("IClass1") != null)
          return Activator.CreateInstance(type) as IClass1;
      }
 
      throw new Exception("no class found that implements interface IClass1");
    }
 
    static void Main(string[] args)
    {
      IClass1 class1 = GetIClass1("ClassLibrary1.dll");
      class1.DoSomething();
    }
  }
}

[Source: http://social.msdn.microsoft.com/forums/en-US/clr/thread/093c3606-e68e-46f4-98a1-f2396d3f88ca/], may not work without change in CF.

Using writing all the C/C++ LoadLibrary/GetProcAddress or the above for all your library functions is very time consuming and error-prone.

There is another way using class interfaces and libraries for hardware dependent code described at MSDN: Barcode Scanners and the Compact Framework. The disadvantage of this solution is that you have to add references to all different possible implementation assemblies: in the example, you have to add a ref to the Intermec barcode scanner implementation and to the symbol barcode scanner. Imagine if you have to manage this for 4 and more manufacturers and not only for barcode scanners but also for other OEM vendor features (WLAN, keyboard).

So, looking for a better solution, I found the Managed Extensibility Framework (MEF) and fortunately a Pocket MEF (the compact framework compatible variant). MEF was developed as a community project with technology previews at codeplex. It now is part of NET 4.0 framework.
Pocket MEF is also hosted a codeplex, it follows the main technology previews until CTP8.1. Although the full MEF has already reached version 2, the Pocket MEF is sufficient to be very useful.

Pocket MEF

I will describe how you can use PocketMEF to write an application that will automatically load the right hardware dependent DLLs. We will use PocketMEF to implement a Hardware Abstraction Layer (HAL). PocketMEF will load the right assemblies during runtime on demand using a file name pattern.

Use Interfaces

This step is also used in the article here. Using interfaces or factory classes ensures that your “plugins” or OEM modules all have the same interface.

Hardware abstraction: Implement the different OEM dependent class libs.
Let PocketMEF load the right class libs.

plug in use: Implement different plugins using the same interface.
Let PocketMEF load all matching plugins.

The Sample Solution MEFdemo

I have done a sample application that shows both, hardware abstraction and plug in usage. The Visual Studio 2008 Windows Mobile 6 Prof. solution defines a main application, two different contracts (interfaces) and some extensions that fullfill the interfaces.

Image 1

Unfortunately, the solution is more complex, as it shows both aspects. OK, here is a design schema:

Image 2

You see, we have one IAppPlugin and one IBarcodeScanControl interface. Then, there are two Forms that implement IAppPlugin and one of that uses an IBarcodeScanControl.

The IAppPlugin Interface

C#
namespace MEFdemo1.AppContracts
{
    //describe a public appPlugin interface (contract)
    public interface IAppPlugin : IComponent
    {
        Bitmap appBitmap{ get; }
        System.Windows.Forms.DialogResult DialogResult { get; }
        string sAppText { get; }
        string sReturnData { get; }
    }
}

An IAppPlugin Implementation

The simple AppPlugin1 Form implements this interface and exports it.

C#
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
 
using System.ComponentModel;
using System.ComponentModel.Composition;
 
using MEFdemo1.AppContracts;
 
namespace AppPlugin1
{
    [PartCreationPolicy(CreationPolicy.NonShared)]
    [Export(typeof(IAppPlugin))]
    public partial class UserForm1 : Form, IAppPlugin
    {
...
[codesyntax]
The AppPlugin2 uses IBarcodeControl
This form imports the IBarcodeControl and exports IAppPlugin.
[codesyntax lang="csharp"]
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
 
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.ComponentModel.Composition.Diagnostics;
 
using MEFdemo1.HAL.DeviceControlContracts;
using MEFdemo1.AppContracts;
 
namespace AppPlugin2
{
    [PartCreationPolicy(CreationPolicy.NonShared)]
    [Export(typeof(IAppPlugin))]
    public partial class BarcodeForm : Form, IAppPlugin
    {
    ...
    }
        //[Import(typeof(IBarcodeScanControl))]
        //if Import is used here, the catalog is unable to find the control!
        private IBarcodeScanControl conScan;
 
        DirectoryCatalog catalog2;
        CompositionContainer container2;
 
        public BarcodeForm()
        {
            InitializeComponent();
            try
            {
                string sPath="";
                if (isIntermec)
                    sPath = "MEFdemo1.HAL.Intermec.*Control*.dll";
                else
                    sPath = "MEFdemo1.HAL.ACME.*Control*.dll";
 
                //I was unable to use the different catalog and let it look in a subfolder
                //so the plug in names are used as a filter
                catalog2 = new DirectoryCatalog(".", sPath);
 
                foreach (string s in catalog2.LoadedFiles)
                    System.Diagnostics.Debug.WriteLine(s);
 
                container2 = new CompositionContainer(catalog2);
 
                container2.ComposeParts(this);
...

The usable BarcodeControl is loaded by using a different file pattern for the DirectoryCatalog. So only one BarcodeControl is loaded.

The MainForm

The main form scans the application dir for IAppPlugins and lists them using their bitmap property.

C#
...
        //we will import all available plugins
        [ImportMany(typeof(IAppPlugin))]
        private int iPluginCount = 0;
 
        DirectoryCatalog catalog;
        CompositionContainer container;
...
        public MainForm()
        {
            InitializeComponent();
            try
            {
                catalog = new DirectoryCatalog(".", "MEFdemo1.Plugins.*.dll");
                container = new CompositionContainer(catalog);
                container.ComposeParts(this);
                drawPlugins();
            }
            catch (Exception ex)
            {
                MessageBox.Show("No Plugins loaded: " + ex.Message);
            }
        }

drawPlugins will draw the images inside MainForm using pictureboxes and applies a click handler for these.

C#
void MainForm_Click(object sender, EventArgs e)
{
    string sApp = ((PictureBox)sender).Tag.ToString();
    System.Diagnostics.Debug.WriteLine(sApp);
    foreach (IAppPlugin iApp in plugins)
    {
        if (iApp.sAppText.Equals(sApp))
        {
            ((System.Windows.Forms.Form)iApp).ShowDialog();
            continue;
        }
    }
}

Here is a screenshot of the composed parts:

Image 3

When you click one of the pictureboxes, the plugins form is shown: either the BarcodeForm or the simple UserForm:

Image 4 Image 5

The programs dir reflects all the contracts and plugins:

C#
intermec.datacollection.cf3.5.dll                      intermec barcode hardware wrapper
intermec.devicemanagement.smartsystem.itcssapi.dll     intermec software wrapper
MEFdemo1.exe                                           the application
mefdemo1.appcontracts.dll                              the AppPlugin contract
mefdemo1.devicecontrolcontracts.dll                    the BarcodePlugin contract
MEFdemo1.HAL.ACME.BarcodeControl2.dll                  one BarcodeControl (the generic implementation)
MEFdemo1.HAL.Intermec.BarcodeControl1.dll              second BarcodeControl (intermec dependent)
MEFdemo1.Plugins.AppPlugin1.dll                        the UserForm application plug in
MEFdemo1.Plugins.AppPlugin2.dll                        the BarcodeForm application plug in
pocket.componentmodel.initialization.dll               PocketMEF runtime
pocket.system.componentmodel.composition.dll           PocketMEF runtime

If you remove one of the AppPlugin DLL files and then start MEFdemo, only the remaining plug in is shown.

Although I implemented all plugins in their own class library projects, you can combine multiple plugins into one project and so will get less DLLs. The disadvantage is that you can't simply update only one of these plugins, you have then to replace the whole lib.

Source code is hosted at code.google.com/p/pocketmef/source/.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)