Click here to Skip to main content
Click here to Skip to main content

Proxy Delegates

, 27 Dec 2002 CPOL
Rate this:
Please Sign up or sign in to vote.
Use reflection and proxy delegates to interface between two separate assemblies.

Introduction

A requirement of the (Application Automation Layer [^]) technology that I’m developing for C# is runtime component plug-ins. One of the issues I am facing is that the components need to register publicly available services with a Component Manager. Since the Component Manager loads the component, it seems reasonable to pass an instance of the Component Manager class to the plug-in so that it can use a public “register service” method. This is easily done in C++, and the plug-ins only need a definition of the Component Manager class.

Unfortunately, this implementation is not as easily achieved in C#. An instance of the Component Manager class cannot be passed to the plug-in without having the Component Manager referenced in the plug-in’s assembly, and this defeats one of the purposes of component development—being able to eliminate build dependencies between components.

The Proxy Class

A better implementation, even in C++, is to use a proxy class that internally knows how to interface to the Component Manager. This allows changes to the Component Manager class without requiring recompilation of the plug-ins.

A simple proxy can similarly be implemented in C# using a delegate, which is actually a type of class. The proxy allows the plug-in to invoke back without requiring specific knowledge of the target interface. The following diagram illustrates the design.

Note that the only pre-requisite is that the plug-in has a defined entry point that the loader can use to initialize the application/plug-in interface.

The Proxy Delegate Implementation

The implementation for the proxy delegate is ridiculously simple:

using System;

namespace Proxy
{
    public delegate void Callback(string s);
}

This implementation must be placed in a DLL that is referenced in the application and plug-in projects.

The Plug In Implementation

This implementation is for demonstration purposes only:

using System;

namespace PlugIn
{
    public class PlugIn
    {
        public static void Test(Proxy.Callback cbck)
        {
            cbck("Success!");
        }
    }
}

This is a very simple demonstration, in which the plug-in invokes a method in the application using a delegate.

Loading The Plug-In

The plug-in is loaded using reflection. I have a fairly generic method that loads a DLL and gets the specified method (I’ve removed try-catch blocks from this for simplicity):

public static MethodInfo GetMethodInfo(string reflection)
{
    MethodInfo mi=null;
    string[] info=reflection.Split(new char[] {'/'});
    Assembly mainAssembly=Assembly.LoadFrom(info[0]);
    Type type=mainAssembly.GetType(info[1]);
    mi=type.GetMethod(info[2]);
    return mi;
}

The plug-in is loaded and the Test method is acquired by calling the GetMethodInfo function:

mi=GetMethodInfo("PlugIn.dll/PlugIn.PlugIn/Test");

Note how the three components of an assembly are parsed, using ‘/’ as a separator.

  • The first component of an assembly is the file name;
  • The second component of an assembly is the namespace and class name;
  • The third component of an assembly is the method name.

The Callback Function

The callback function is a simple test function that sets the text of an edit box to the string passed in by the plug-in:

private void CallbackTest(string s)
{
    textBox1.Text=s;
}

Creating The Delegate

A delegate is instantiated with one line:

Proxy.Callback cbck=new Proxy.Callback(CallbackTest);

Invoking The Plug-In’s Test Method

The Test method of the plug-in expects the delegate as a parameter. This is established by creating a parameter list:

object[] parms=new object[] {cbck};

The plug-in function is called using the reflection Invoke method:

mi.Invoke(null, BindingFlags.Public | 
  BindingFlags.NonPublic | BindingFlags.InvokeMethod 
  | BindingFlags.Static, null, parms, null);

The Demo Program

I have put together a very simple demonstration program.

Click on the “Load Plug In” button to load the PlugIn.dll assembly. Click on the “Invoke” button to call into the plug-in using reflection and to have the plug-in call back to the executable using the proxy delegate.

For simplicity, compile only in Debug mode. The projects are set up to place all the DLLs in the ProxyDemo/bin/Debug directory.

Notes About This Implementation

In this implementation, the plug-in’s method must be static. An instance of an object cannot be used because this would necessitate referencing the plug-in assembly, which we are trying to avoid. Therefore, the first parameter of the Invoke method is null, since there is no object on which to invoke the method (and in fact, this parameter is ignored for static method invocations). However, as demonstrated by this code, the delegate can reference a method associated with a specific instance.

Why Use This?

Unlike marshalling, which is intended to operate with unmanaged memory and COM objects, this approach is instead very simple when you have complete control over the plug-in interface. It promotes modular implementations and allows components to be individually unit tested. For example, a test jig could be created separate from the application for the purposes of component testing.

References

License

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

Share

About the Author

Marc Clifton

United States United States
Marc is the creator of two open source projets, MyXaml, a declarative (XML) instantiation engine and the Advanced Unit Testing framework, and Interacx, a commercial n-tier RAD application suite.  Visit his website, www.marcclifton.com, where you will find many of his articles and his blog.
 
Marc lives in Philmont, NY.

Comments and Discussions

 
GeneralDid anyone try this? An unmanaged C++ EXE register a callback in a C# DLL and then the C# DLL invokes the callback. Pinmemberhihint7-Apr-04 23:17 
GeneralAvoid Proxy.dll PinmemberPetru6628-Mar-04 23:02 
QuestionWhats the role of the delegate? Pinmemberleppie30-Dec-02 11:29 
AnswerRe: Whats the role of the delegate? PinmemberMarc Clifton30-Dec-02 13:56 
GeneralAdvil Please! PinmemberDavid Stone30-Dec-02 6:03 
GeneralRe: Advil Please! PinmemberMarc Clifton30-Dec-02 6:06 
GeneralRe: Advil Please! PinmemberDavid Stone30-Dec-02 6:30 
QuestionWhy not use an interface? PinmemberFrank Hileman29-Dec-02 18:02 
AnswerRe: Why not use an interface? PinmemberMarc Clifton30-Dec-02 1:26 
GeneralRe: Why not use an interface? PinmemberFrank Hileman30-Dec-02 7:25 
GeneralRe: Why not use an interface? PinmemberMarc Clifton30-Dec-02 7:29 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.141015.1 | Last Updated 28 Dec 2002
Article Copyright 2002 by Marc Clifton
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid