.NET

CInject – Code Inject

CInject (or CodeInject) allows code injection into any managed assembly without disassembling and recompiling it. It eases the inevitable task of injecting any code in single or multiple methods in one or many assemblies to intercept code for almost any purpose.

When using CInject, you do not require any knowledge of the target application. You can create your own injectors very easily and inject them in any target assembly/executable. An example is provided with this application.

It is developed in C# and uses Mono.Cecil for code interception and log4net for logging purposes. CInject uses GNU GPL v3 license and source code is checked in at CodePlex website.

Download from http://codeinject.codeplex.com/releases/

Plugins? Yes, you can now create CInject plugins to customize CInject for your needs. To develop a plugin, you do not need to master C# language, or understand Reflection, or Mono Cecil. All you need to know is concepts of inheritance, and a little about working of CInject. So lets not beat around the bush and get to the point.

How do I create a plugin?

To create your own plugin, create a Class Library project in Visual Studio 2010. Let’s name the project as CInject.Plugin.Sample. The project output will be an assembly whose name should match the format

CInject.Plugin.*.dll

So lets right click the Visual Studio Project and click on Properties. In the tab “Application”, change the Assembly Name to CInject.Plugin.SamplePlugin. Next, we need to add reference to some assemblies

Adding Project References

  • CInject.PluginInterface.dll – This assembly is shipped with CInject and exposes interface IPlugin that we need to implement
  • System.ComponentModel.Composition.Codeplex – This assembly can be downloaded from CodePlex (MEF), or from the CInject package.
  • System.Drawing – This assembly is required for implementing Plugin Menus.
  • System.Windows.Form – This assembly is required as CInject is a Windows Application

Implemeting IPlugin

Let’s create a class called SamplePlugin.cs. The name of the class has nothing to do with the name of the plugin so you can name it as you want. This class needs to be decorated with Export attribute of MEF framework (found in namespace System.ComponentModel.Composition). The Export attribute has 2 constructors, but for CInject we will use the constructor that takes one parameter i.e. Type. So we decorate it with

[Export(typeof(IPlugin))]

Since we are developing the plugin for CInject, the class needs to implement the interface IPlugin and should have a valid Name, Version, Menu and an implementation of OnMessageReceived and HandleError. Let’s understand the significance of the each member of IPlugin interface

  • Name – This is the name of the plugin [To be used in the next release]
  • Version – This is the version of the plugin [To be used in the next release]
  • Menu – If you want your plugin to display a Menu you can implement it; othewise, you can choose to return a NULL value (as shown in the code below)
  • OnStart – This method is called when the plugin is loaded into memory
  • OnClose – This method is called when the plugin is unloaded from the memory. This method should clean up the resources from the memory to allow successful closure of the application
  • OnMessageReceived –This method receives the events & messages from the CInject application. This method should ideally contain logic to handle the events and perform some plugin related logic
  • HandleError –Any technical unhandled errors raised while execution of OnMessageReceived, OnStart, and OnClose will be caught in HandleError method. This method should not throw any exception and should be written very carefully

SamplePlugin code

using CInject.PluginInterface;
using System.ComponentModel.Composition;
namespace CInject.Plugin.Sample
{
    [Export(typeof(IPlugin))]
    public class SamplePlugin : IPlugin
    {
        public string Name
        {
            get { return “SamplePlugin”; }
        }
        public string Version
        {
            get { return1.0?; }
        }
        public System.Windows.Forms.ToolStripMenuItem Menu
        {
            get
            {
                return null; // NULL – plugin does not have any menu
            }
        }
        public void OnMessageReceived(EventType eventType, Message message)
        {
            // Implement logic for Message Received from CInject Application            
        }
        public void HandleError(System.Exception exception)
        {
            // This should never throw any exception            
        }
        public void OnStart()
        {
            // Should read configuration, initialize variables, etc
        }
        public void OnClose()
        {
            // Should clean up the memory resources
        }
    }
}

Handling the events & messages

  • There are multiple events that CInject application raises. Let’s have a quick look at them
  • ProcessStart – Called when overall processing starts
  • MethodInjectionStart – Called before an assembly method is injected
  • MethodInjectionComplete – Called after an assembly method is injected
  • ProcessComplete – Called when overall processing completes
  • TargetAssemblyLoaded – Called when a target assembly is loaded
  • InjectionAssemblyLoaded – Called when an injection assembly is loaded
  • ApplicationStarted – Called when CInject application has started
  • ApplicationClosing – Called when CInject application is closing
  • Error – Called when an error occurs within CInject application. This is different from HandleError that handles the unhandled exceptions raised by plugin code

OnMessageReceived code

public void OnMessageReceived(EventType eventType, PluginInterface.Message message)
        {
            switch (eventType)
            {
                case EventType.ApplicationClosing:
                case EventType.ApplicationStarted:
                    break;
                    // Here Error property will be null
                    // Only Result will be non-null  
                    MessageBox.Show(“I received a injection related message: “ + message.Result);
                case EventType.TargetAssemblyLoaded:
                case EventType.InjectionAssemblyLoaded:
                    break;
                    // Here Error property will be null
                    // Only Result will be non-null with injector/target assembly path
                    MessageBox.Show(“I received a injection related message: “ + message.Result);
                case EventType.MethodInjectionStart:
                case EventType.MethodInjectionComplete:
                    // Here Error property will be null
                    // Expect Target, Injector will be non-null
                    MessageBox.Show(“I received a injection related message: “ + message.ToString());
                    break;
                case EventType.ProcessComplete:
                case EventType.ProcessStart:
                    // Here Error property will be null
                    // Expect only the Result to be non-null
                    MessageBox.Show(“I received a process related message: “ + message.Result);
                    break;
                case EventType.Error:
                    MessageBox.Show(“I received an error message: “ + message.Error);
                    break;
            }
        }

Compiling and deploying

You can now compile the class library to create a plugin named CInject.Plugin.SamplePlugin.dll and deploy it in Plugins sub-folder in the CInject application. The CInject application automatically scans for

  • Assemblies that match the name pattern CInject.Plugin.AnyNameHere.dll
  • Searches for IPlugin implementation(s) with Export attribute within the searched assembly CInject.Plugin.AnyNameHere.dll
  • Loads them in memory and raises events OnStart, OnMessageReceived (with EventType ApplicationStarted) in same order
  • Unloads them from memory when application is closed and raises events OnClose, OnMessageReceived (with EventType ApplicationClosing) in same order

Related Posts:

  • Complete Guide to Lazy Loading – Lazy<T>
  • Exception Message Box in C#
  • Using Smart Client with Enterprise Library 4
  • Bluetooth with C#
  • Only Numbers in TextBox

Reference: CInject – Extending code injection with plugins from our NCG partner Punit Ganshani at the Punit Ganshani blog blog.

Related Articles

Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button