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

Simple Plug-in Architecture in Plain C

By , 25 May 2012
 

Introduction

This article explains how to write an application using architecture that supports plug-ins. We would be doing this using plain C. The concepts that are explained can be used to implement in any language. The reader is expected to have a thorough understanding of function pointers and dynamic loading of dlls. Knowledge of the GTK toolkit would be a plus but not mandatory to understand the concepts. You can download the GTK development environment installer from http://downloads.sourceforge.net/gladewin32/gtk-dev-2.12.9-win32-2.exe 

Background

A plug-in is a software component that can be loaded by a host application at run-time or during startup. The plug-in provides additional features to the application. However, a plug-in also needs to have some access to the host application in order to make the application features customizable using plug-ins. We would be using the dynamic loading mechanism of DLLs in order to implement the plug-in architecture. The entire architecture, the application and the plug-ins will be implemented in plain C. The GUI is implemented in GTK+ to make it cross-platform.

The Architecture

Any application that supports dynamic loading of software components is intended to be made so to enable customization and/or enhancement of the existing application without having to have it recompiled or, as that matter of fact, have access to its source code. And to have an application support such a feature, it should be written to have at least the ability to:
1) Load the plug-in modules at run time. (Dynamic loading of DLLs)
2) Identify modules those are of its predetermined type.
3) Query for minimal functions and objects from the module.
4) Update its interface to enable the user to use it with the added functionality via menus or icons.


Shown below is typical plug-in based software architecture…

Loading the Plug-in

Typically, applications use a predetermined folder path and expect the plug-in developers to place the plug-ins in that path so that it can load them from there at runtime or during startup. Some applications may also provide interactive dialogs (plug-in managers) to add plug-in paths and save it to application configuration files. The application loads the DLL files using the standard OS API calls for dynamically loading libraries like LoadLibrary on Windows and dlopen on UNIX/Linux. The validity of the plug-in is verified by making calls to certain functions expected to be implemented by the plug-in developer.

Identifying plug-in types

The application may support plug-in modules for a variety of operations. These operations may be categorized into different types. The application will also query the module for its type. Based on this query, the application will decide to make it available for the user through the corresponding tool-box, tool-bar or menu category.

The API

For all this to work, the plug-in developer must be given some protocol to follow when the plug-in is written. This is enforced by having the developer implement certain functions and have them return certain predefined values. However, this is only half part of the game. By having the developer implement those functions only the application is gaining control over the plug-in module. The plug-in yet doesn’t have control over the application without which there is no point in having it to do something useful to manipulate the application. Again this has to be provided by the application. But how does the plug-in developer link to the code in the application if it is built as a monolithic piece of code. For the developer to be able to call functions to control the application, those functions must be written in a separate module that both the application and the plug-in can share. So, essentially most of the functionality that the application wishes to share with the plug-ins must be detached from the host application. This set of application functionality and the plug-in protocols together put into a sharable module and interface makes the application’s Application Programming Interface (API) or Software Development Kit (SDK).


The Implementation

Let us now have a look at how to implement such and API, its hosting application and the plug-in modules. For convenience of implementation for this simple demonstration, most of the application’s functionality including the GUI is kept in the API with exported functions to access and modify it. Shown below is a schematic representation of the implementation.

The application is a simple OpenGL application that draws a torus on the screen. The API exposes functions to modify the attributes of the OpenGL viewer and the torus displayed in it. The host application which is the main executable invokes the main GUI from the API and keeps it up and running. The GUI is available via a static handle throughout the lifecycle of the application. This allows the GUI to be customizable via plug-ins. Editing the torus to change its attributes is done by a plug-in that gets added to the menu if it is made available in the plugins folder. A dialog box implemented in the plug-in provides control elements to set the attribute values which are applied to the torus by accessing the corresponding functions from the API. The OpenGL viewer is also exposed by the API via a static handle that enables the plugin developer to get hold of it to modify the viewer attributes like background color or the entities displayed in it.


We must remember that dynamically loading the plugin module and making it available in the application via the menu is only half side of the story. For the other half, in which the code in the plugin module will be able to control the objects in the application binary, the application itself must be implemented in the API so that the plugin developer can link the modules in which the application components exist. The reason for making the core application objects available via static handles in the API is that the plugins must be able to access the running instances of those objects and access the application functionality via them. This is possible because when the running application loads the plugin into its own process space the static objects referred by the API, the plugin modules and the host application would be the same.

To keep things as simple as possible and intuitive we would be using simple C structures and functions operating on those structures to represent the plugin interface, the application functionality and the host application itself. Of course, using C++ and an Object Oriented paradigm will make large scale application development more organized, modular and maintainable but to get through the raw concepts it would be easier to have it implemented in C.

Let us have a look at the plugin header interface. Our plugin interface is represented by the structure _PluginStruct which holds nothing more than a few useful function pointers.

struct _PluginStruct
{
    NAMEPROC nameProc;
    PROVIDERPROC providerProc;
    MENUPROC menuProc;
    MENUCATPROC menuCatProc;
    RUNPROC runProc;
    DESTROYPROC destProc;
};
typedef struct _PluginStruct PluginStruct, *LPPLUGINSTRUCT;

The function pointers are to those functions expected to be implemented by the plugin developer.

typedef LPPLUGINSTRUCT (*CREATEPROC) (void);
typedef void (*DESTROYPROC) (LPPLUGINSTRUCT);
typedef const gchar* (*NAMEPROC) (void);
typedef const gchar* (*PROVIDERPROC)(void);
typedef const gchar* (*MENUPROC) (void);
typedef const gchar* (*MENUCATPROC) (void);
typedef void (*RUNPROC) (void);

The functions are for creating, destroying, getting plugin category information, menu information and executing the plugin functionality.

PLUGINAPP_API LPPLUGINSTRUCT plugin_app_create_plugin(void);
PLUGINAPP_API void plugin_app_destroy_plugin(LPPLUGINSTRUCT);
PLUGINAPP_API const gchar* plugin_app_get_plugin_name(void);
PLUGINAPP_API const gchar* plugin_app_get_plugin_provider(void);
PLUGINAPP_API const gchar* plugin_app_get_menu_name(void);
PLUGINAPP_API const gchar* plugin_app_get_menu_category(void);
PLUGINAPP_API void plugin_app_run_proc(void);

Then we have static handles defined for the Application frame – the main window and for the OpenGL viewer…

static GtkWidget* _mainwindow = NULL;
static LPGLVIEW   _glview = NULL;


…and related query functions…

PLUGINAPP_API GtkWidget* plugin_app_get_mainwindow();
PLUGINAPP_API LPGLVIEW plugin_app_get_glview();
PLUGINAPP_API void plugin_app_get_torus_data(LPGLVIEW iView, double* majorRadius, double* minorRadius, int* r, int* g, int* b, BOOL* shaded);
PLUGINAPP_API void plugin_app_set_torus_data(LPGLVIEW iView, double majorRadius, double minorRadius, int r, int g, int b, BOOL shaded);

These funtions provide means to access the application objects like the main window, the viewer, the displayed 3D object etc. They also facilitate the manipulations of those objects.
That is the end of the simple plugin interface header.
Now let us have a look at the implementation of the plugin loading process in the core API. The header that provides the functions to load, unload and maintain the plugins has the following functions…

PLUGINAPP_API void   plugin_helper_add_plugin_directory (const gchar *directory);
PLUGINAPP_API void   plugin_helper_find_plugins_in_directory();
PLUGINAPP_API GList* plugin_helper_get_plugin_list();
/* Use these functions to load/unload plugins. */
PLUGINAPP_API void*  plugin_helper_load_plugin (const gchar *filename);
PLUGINAPP_API void   plugin_helper_unload_plugin(void* handle);


Let us have a look into the function that loads the plugin…

void* plugin_helper_load_plugin(const gchar *filename)
{
    gchar *pathname = NULL;
    void *plugin = NULL;
    if (!filename || !filename[0])
        return NULL;
    pathname = plugin_helper_find_plugin_file (filename);
    if (!pathname)
    {
        g_warning (_("Couldn't find plugin file: %s"), filename);
        return NULL;
    }
    plugin = (void*)LoadLibrary(pathname);
    g_free (pathname);
    return plugin;
} 

The Windows API function LoadLibrary loads the plugin dll into the memory and returns a handle to it from the function shown above.
When the main GUI of the application is shown, the Show event loads all the plugins available in a predefined folder. Shown below is the code that loads and exposes the plugins…

void load_all_plugins(GtkWidget *widget, gpointer user_data)
{
    LPPLUGINSTRUCT pls = NULL;
    CREATEPROC create = NULL;
    MENUPROC menuproc = NULL;
    MENUCATPROC menucatproc = NULL;
    RUNPROC runproc = NULL;
    …
    …
    void* handle = NULL;
    GtkWidget* mw = plugin_app_get_mainwindow();
    editmenuitem = lookup_widget(mw, "editmenuitem");
    insertmenuitem = lookup_widget(mw, "insertmenuitem");
    elem = plugin_helper_get_plugin_list();
    while (elem)
    {
        filename = (gchar*)elem->data;
        handle = plugin_helper_load_plugin(filename);
        if(handle)
        {
            create = (CREATEPROC) GetProcAddress(handle, "plugin_app_create_plugin");
            if ((error = GetLastError()) != 0)
            {
                …
                …
            }
            else
            {
                /* store the handle for later use */
                plugin_handles = g_list_prepend(plugin_handles, handle);
                /* Create an instance of the plugin struct */
                pls = create();
                if(pls && pls->menuProc && pls->runProc)
                {
                    /* store the plugin struct for deletion at exit */
                    plugin_structs = g_list_prepend(plugin_structs, pls);
                    menuproc = (MENUPROC) pls->menuProc;
                    menuName = (gchar*)menuproc();
                    menucatproc = (MENUCATPROC) pls->menuCatProc;
                    menuCategory = (gchar*)menucatproc();
                    g_message (_("Creating menu item: %s"), menuName);
                    menu = gtk_image_menu_item_new_with_label (menuName);
                    gtk_widget_set_name (menu, menuName);
                    gtk_widget_show (menu);
                    g_object_set_data_full (G_OBJECT (mw), menuName,
                    gtk_widget_ref (menu), (GDestroyNotify) gtk_widget_unref);
                    if(g_strcmp0(menuCategory, "edit") == 0)
                    {
                        gtk_container_add (GTK_CONTAINER (editmenuitem), menu);
                    }
                    else
                    {
                        gtk_container_add (GTK_CONTAINER (insertmenuitem), menu);
                    }
                    runproc = (RUNPROC)pls->runProc;
                    g_signal_connect(G_OBJECT(menu), "activate", G_CALLBACK(runproc), NULL);
                }
                else
                {
                    g_printerr("Invalid Plugin Structure!");
                }
            }
        }
        elem = elem->next;
    }
    gtk_widget_show_all(widget);
} 

The function above loads all the valid plugins, creates menus with appropriate names, attaches the plugin’s execute function pointer to the menu handler and exposes it in the relevant category. This is where the function pointers in the plugin structure comes handy. The function pointers in the plugin structure are either used for querying or to attach as callbacks to the menus. They are also called directly for the plugin execution.
When the application quits the plugins are gracefully unloaded and destroyed.

Remember, all what we discussed was the meat of the API. We haven’t yet seen the actual executable application that will instanciate the application main window and have it running. Let’s have a look at it…

int main (int argc, char *argv[])
{
    GtkWidget *MainWindow;
    char* exeName = getexepath();
    gchar* exePath = g_path_get_dirname(exeName);
    gchar* pluginPath = g_strdup_printf("%s%s", exePath, "/../plugins");
    …
    …
    …
    gtk_init (&argc, &argv);
    add_pixmap_directory (PACKAGE_DATA_DIR "/" PACKAGE "/pixmaps");
    plugin_helper_add_plugin_directory (pluginPath);
    // Find plugins in the plugin directory
    plugin_helper_find_plugins_in_directory();
    // This is where the main window gets instanciated
    // The function returns the static MainWindow object
    // after creating it if necessary. 
    MainWindow = plugin_app_get_mainwindow ();
    gtk_widget_show (MainWindow);
    g_signal_connect(G_OBJECT(MainWindow), "destroy", _CALLBACK(on_quit1_activate), NULL);
    gtk_main ();
    return 0;
}

The executable simply sets the plugin folder path and checks for available plugins and maintains a list of the valid plugin paths. Then it instanciates the application main window and sets it up and running which in turn loads and exposes the plugins.

Now let us have a peek into a simple plugin that controls the graphic attributes of the displayed torus…

Torusdlg/pluginimpl.c

#include <pluginappsdk.h>
#include "interface.h"
...
...
static GtkWidget* pDlg1 = NULL;
...
LPPLUGINSTRUCT plugin_app_create_plugin()
{
    LPPLUGINSTRUCT PLS = (LPPLUGINSTRUCT)malloc(sizeof(PluginStruct));
    g_debug("TorusDlg::plugin_app_create_plugin");
    if(!PLS)
    {
        return NULL;
    }
    PLS->nameProc = plugin_app_get_plugin_name;
    PLS->providerProc = plugin_app_get_plugin_provider;
    PLS->menuProc = plugin_app_get_menu_name;
    PLS->menuCatProc = plugin_app_get_menu_category;
    PLS->runProc = plugin_app_run_proc;
    PLS->destProc = plugin_app_destroy_plugin;
    return PLS;
}
void plugin_app_run_proc()
{
    pDlg1 = create_TorusDlg();
    gtk_window_set_transient_for(GTK_WINDOW(pDlg1), GTK_WINDOW(plugin_app_get_mainwindow()));
    gtk_widget_show(pDlg1);
}

We first implement the plugin_app_create_plugin() function in which we create a plugin structure instance and assign the pointers to the other functions we implement to the function pointer members in the structure.

The plugin_app_run_proc() function only creates the torus edition dialog and displays it. The modification of the attributes of the torus is done on the Apply buttons click event.

void on_applybutton1_clicked(GtkButton *button, gpointer user_data)
{
    GtkWidget* wid;
    GdkColor color;
    gdouble min, maj;
    gboolean shaded;
    ...
    LPGLVIEW view = plugin_app_get_glview();
    ...
    wid = lookup_widget(GTK_WIDGET(button), "colorbutton1");
    gtk_color_button_get_color(GTK_COLOR_BUTTON(wid), &color);
    ...
    wid = lookup_widget(GTK_WIDGET(button), "spinbutton1");
    maj = gtk_spin_button_get_value(GTK_SPIN_BUTTON(wid));
    wid = lookup_widget(GTK_WIDGET(button), "spinbutton2");
    min = gtk_spin_button_get_value(GTK_SPIN_BUTTON(wid));
    wid = lookup_widget(GTK_WIDGET(button), "checkbutton1");
    shaded = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(wid)); 
    ...
    if(view)
    {
        plugin_app_set_torus_data(view, maj, min, color.red/257,color.green/257,color.blue/257, shaded);
    }
}

We get the OpenGL view static handle using the line...

LPGLVIEW view = plugin_app_get_glview();

...and use it to manipulate the torus attributes. The magic lies in the developer being able to compile this unit as a seperate module and link to the API and the module code being able to access the application objects once it is loaded into the application's process space.

A couple of plugins have been implemented to demonstrate the loading of multiple plugins in different category. Explaining everything would make this article too long. I hope I have provided enough insight to the concepts. The accompanying source code will definitely provide more light to the matter. The reader is encouraged to debug through the code and see the sequences of the function calls.

A plugin manager dialog shows all the loaded plugins and also allows the user to reload the plugins thus enabling runtime plugging of software modules into the application.

The plugins built in the solution automatically get copied to a folder named plugins. If you only build and run the pluginapp project for the first time you will not see any plugins in the list box. Once you build all other projects in the solution the plugins will be generated and copied to the plugins folder. You need not close the application to have the plugins loaded. Just click on the Reload button on the plugin manager dialog and all the plugins available in the plugins folder will be loaded and the corresponding menus will be populated.

I hope this article has provided a good starting point for understanding plugin based software archirecture.

Points of Interest

The basic mantra of writing plugin based software lies in providing the application the ability to dynamically load dlls/shared objects and having the application functionality built into the API instead of the executable. The executable is usually light weight and just calls the static application object from the API (thereby instanciating it for the first time) and exposes it to the user. 

Tip 

To build the accompanying source code you need to install GTK from the link provided in the beginning of the article and also set the following include and library folder paths in Visual Studio:

Include: 

C:\GTK\include; C:\GTK\include\atk-1.0; C:\GTK\include\glib-2.0; 

C:\GTK\include\gtk-2.0; C:\GTK\include\cairo; C:\GTK\include\pango-1.0; 

C:\GTK\include\gtkglext-1.0; C:\GTK\lib\glib-2.0\include; 

C:\GTK\lib\gtk-2.0\include; C:\GTK\lib\gtkglext-1.0\include;

Library:

C:\GTK\lib    

History

First Version 22 May 2012

Added GTK development environment installer link  23 May 2012

Added tip to set up GTK on Visual Studio.  24 May 2012 

License

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

About the Author

Sharjith
Engineer Tata Technologies Inc
United States United States
Member
Sharjith is a Mechanical Engineer with strong passion for Automobiles, Aircrafts and Software development.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMy vote of 5memberMihai MOGA16 Jun '12 - 20:11 
This is a great inspiring article. I am pretty much pleased with your good work. You put really very helpful information. Keep it up once again.
GeneralRe: My vote of 5memberSharjith17 Jun '12 - 3:47 
Thank you!
RegardsSmile | :)
N. Sharjith

QuestionImage is not getting displayed after Addons Fire on IEmemberMember 453108523 May '12 - 4:26 
Hello everyone,
As i am currently working on Internet Explorer Addons, when my addons get fired image in any website is not getting displayed and progress bar continue displaying.
My Addons purpose is to search and Highlight the text which matches from my database dictonary.
 
My code is as below:
void OnDocumentComplete(object pDisp, ref object URL)
        {
            //if (!ReferenceEquals(pDisp, URL))
            //{
            //    return;
            //}

 
            // ole connection object 
            OleDbConnection objConn = null;
            //Passing Query String and Ole connection object to Ole DB Command
            OleDbCommand cmd = new OleDbCommand();
            //Declaring OleDbDataAdapter object which execute the OleDbCommand in next line.
            OleDbDataAdapter oleda = new OleDbDataAdapter();
            //Creating Query String
            string querystr = "";
            //Declaring new Data table with dt object
            DataTable dt = new DataTable();
 
            WordS_A_Text_Service obj = new WordS_A_Text_Service();
            string myHost = "", IPAddress = "", Web_URL = "";
            int i = 0, TotalWordCount = 0;
            try
            {
                var document = browser.Document as IHTMLDocument2;
                var window = document.parentWindow;
                myHost = System.Net.Dns.GetHostName();
                IPAddress = System.Net.Dns.GetHostByName(myHost).AddressList[0].ToString();
                Web_URL = document.url;
                string script = string.Empty;
                script = @"
 
                
                var xdr;
                function getXMLHttpRequest() 
                {
                    if (window.XMLHttpRequest) 
                    {
                        return new window.XMLHttpRequest;
                    }
                    else
                    {
                        try 
                        {
                            return new ActiveXObject('MSXML2.XMLHTTP.3.0');
                        }
                        catch(ex) 
                        {
                            return null;
                        }
                    }
                }
 
                function handler()
                {
                    if (oReq.readyState == 4 /* complete */)
                    {
                        if (oReq.status == 200) 
                        {
                            alert(oReq.responseText);
                        }
                    }
                }
 
                function err()
                {
                    /* alert('XDR onerror'); */ 
                }
                function timeo()
                {
                   /*  alert('XDR ontimeout'); */
                }
                function loadd()
                {
                   /*  alert('XDR onload'); */
                    /* alert('Got: ' + xdr.responseText); */
                }
                function progres()
                {
                   /*  alert('XDR onprogress'); */
                   /*  alert('Got: ' + xdr.responseText); */
                }
 
                function stopdata()
                {
                   /*  xdr.abort(); */
                }
 
                function SaveMouseOVerWords(OnMouseOverWord)
                { 
                   var c=document.getElementById(OnMouseOverWord);
                   var OnMouseOverDiscription=c.getAttribute('mytitle');
 
                    document.getElementById('divword').innerText = OnMouseOverWord;
                    document.getElementById('divmeaning').innerText = OnMouseOverDiscription;
                    ShowContent('divPopUp');
                    var PageID=document.getElementById('hdnRecordID').value;
                    try
                    {
                        xdr = new XDomainRequest();
                        if (xdr)
                        {
                            xdr.onerror = err;
                            xdr.ontimeout = timeo;
                            xdr.onprogress = progres;
                            xdr.onload = loadd;
                            xdr.timeout = 15000;
                            xdr.open('get', 'http://web.metaoptionllc.com/WordsAText/default.aspx?Word='+OnMouseOverWord +'&RecordID='+PageID); 
                            xdr.send();
                        }
                       
                    }
                    catch(ex)
                    {
                        /* alert(ex.message+' '+ex.name+' '+ex.linenumber); */
                    }
                }
 

                var cX = 0; var cY = 0; var rX = 0; var rY = 0;
               function UpdateCursorPosition(e){ cX = e.pageX; cY = e.pageY;}
                function UpdateCursorPositionDocAll(e){ cX = event.clientX; cY = event.clientY;}
                if(document.all) { document.onmousemove = UpdateCursorPositionDocAll; }
                else { document.onmousemove = UpdateCursorPosition; }
                function AssignPosition(d) {
                if(self.pageYOffset) {
	                rX = self.pageXOffset;
	                rY = self.pageYOffset;
	                }
                else if(document.documentElement && document.documentElement.scrollTop) {
	                rX = document.documentElement.scrollLeft;
	                rY = document.documentElement.scrollTop;
	                }
                else if(document.body) {
	                rX = document.body.scrollLeft;
	                rY = document.body.scrollTop;
	                }
                if(document.all) {
	                cX += rX; 
	                cY += rY;
	                }
                d.style.left = (cX+10) + 'px';
                d.style.top = (cY+10) + 'px';
                }
                function HideContent() {
                 var d='divPopUp';
                if(d.length < 1) { return; }
                document.getElementById(d).style.display = 'none';
                }
                function ShowContent(d) {
 
                var elem=document.getElementById('divPopUp');
                elem.style.visibility = 'visible';
                var root = document.documentElement || document.body;
                elem.style.top = event.y + root.scrollTop - elem.offsetHeight;
                elem.style.left = event.x + root.scrollLeft - elem.offsetWidth;
 
                if(d.length < 1) { return; }
                var dd = document.getElementById(d);
               AssignPosition(dd);
 

                dd.style.display = 'block';
                }
                ";
                window.execScript(script);
                var PageText = document.body.innerHTML;
                //Physical Paht of Excel File 
                //string FileName = System.Environment.GetFolderPath(System.Environment.SpecialFolder.ProgramFiles) + "\\DataDriven\\WordS-A-Text\\Dictionary.xls";
                string FileName = System.Environment.GetFolderPath(System.Environment.SpecialFolder.ProgramFilesX86) + "\\Dictionary\\Dictionary.xls";
                //Creating Connection String
                String connString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source= " + FileName + " ;Persist Security Info=False;Extended Properties=Excel 8.0;";
                //Passing Connection String Value into Ole Connection Object
                objConn = new OleDbConnection(connString);
                //Connection is getting open from here
                objConn.Open();
                //Creating Query String
                querystr = @"SELECT *  from [DictionaryFile$] where CHECK_DIGIT > 0";// Record_Level < 501 and
                //querystr
                //Passing Query String and Ole connection object to Ole DB Command
                cmd = new OleDbCommand(querystr, objConn);
                //Passing Command Value to OldDbDataAdapter object
                oleda.SelectCommand = cmd;
                // Filling OleDataAdapter value in DataTable Object dt
                oleda.Fill(dt);
                if (dt.Rows.Count > 0)
                {
                    string TextToHighlight = "", TextAsDiscription = "";
                    int count = dt.Rows.Count;
                    for (i = 0; i < count; i++)
                    {
                        //Pipe = Pipe + dt.Rows[i]["words"].ToString().ToLower() + "|";
                        TextToHighlight = "";
                        TextAsDiscription = "";
                        TextToHighlight = dt.Rows[i]["words"].ToString().ToLower();
                        TextAsDiscription = dt.Rows[i]["Definition"].ToString().ToLower();
                        if (Regex.IsMatch(PageText, string.Format(@"\b({0})\b", TextToHighlight)))
                        {
                            TotalWordCount++;
                            PageText = HighlightSearchKeyWords(TextToHighlight, TextAsDiscription, PageText);
                        }
                    }
                    // Remove last Character
                    //Pipe = Pipe.Remove(Pipe.Length - 1, 1);
                }
                
                //Regex r = new Regex("\b('" + Pipe + "')\b", RegexOptions.IgnoreCase);
                //string s = r.Replace(PageText, new MatchEvaluator(MatchEval));
                
                //document.body.innerHTML = s;
                document.body.innerHTML = PageText;
                RecordID = obj.Save_WordS_A_Text_Details(IPAddress, Web_URL, TotalWordCount);
                string hdnRecordID = "<input id='hdnRecordID' type='hidden' value='" + RecordID + "' />  <div id='divPopUp' style='Z-index:9999; display:none;position:absolute;width:300px;padding: 15px;background: #671329;color: white;font-family:tahoma;border-radius:1em 1em 1em 1em;font-size: 12px;opacity:.85;z-index:100000'><div style='position:absolute;top:0;right:0; float:right;'><img src='Images/Close.jpg' onclick='javascript:HideContent();'/></div><div id='divword' style='height:20px;text-transform:uppercase'></div><hr/><div id='divmeaning' style='height:180px;overflow-y:auto;padding:5px;'></div> </div>";
                document.body.insertAdjacentHTML("afterBegin", hdnRecordID);
 
            }
            catch (Exception e)
            {
                obj.InsertErrorLog(IPAddress, Web_URL, "OnDocumentComplete in BHO", e.Message.ToString());
            }
            finally
            {
                objConn.Close();
                objConn = null;
                querystr = "";
                cmd = null;
                oleda = null;
                dt.Dispose();
            }
        }
        //--------------------------------------------------------------------------------------------------------

        private string MatchReplacement = "";
        private string HighlightSearchKeyWords(string TextToHighlight, string TextAsDiscription, string text)
        {
            string ProcessedText = text;
            var pattern = keywordPattern(TextToHighlight);
            string TagExpression = "<.[^>]*>";
            Regex reg = new Regex("(" + TagExpression + ")|(" + pattern + ")", RegexOptions.IgnoreCase | RegexOptions.Multiline);
            //MatchReplacement = "<span id='" + TextToHighlight.ToString() + "' style='background-color: yellow; cursor: hand;' onclick='javascript:FncAddedByAddon(this.title)' title='" + TextAsDiscription + "'>" + TextToHighlight.ToString() + "</span>";
            MatchReplacement = "<span id='" + TextToHighlight.ToString() + "' style='background-color: yellow; cursor: hand;' onmouseover='javascript:SaveMouseOVerWords(this.id)' mytitle='" + TextAsDiscription + "'>$1</span>";//onmouseout='javascript:HideContent()' 
            ProcessedText = reg.Replace(ProcessedText, new MatchEvaluator(MatchEval));
            return ProcessedText;
        }
 
        private string keywordPattern(string searchKeyword)
        {
            var keywords = searchKeyword.Split(',').Select(k => k.Trim()).Where(k => k != "").Select(k => Regex.Escape(k));
            return @"\b(" + string.Join("|", keywords) + @")\b";
        }
        protected string MatchEval(Match match)
        {
            if (match.Groups[2].Success)
            {
                return Regex.Replace(match.ToString(), "(.+)", MatchReplacement);
            }
            return match.ToString();
        }
        //protected string MatchEval(Match match)
        //{
        //    if (match.Success)
        //    {
        //        string Disc = "";
        //        string FileName = System.Environment.GetFolderPath(System.Environment.SpecialFolder.ProgramFiles) + "\\DataDriven\\WordS-A-Text\\Dictionary.xls";
        //        String connString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source= " + FileName + " ;Persist Security Info=False;Extended Properties=Excel 8.0;";
        //        OleDbConnection objConn = new OleDbConnection(connString);
        //        objConn.Open();
        //        string querystr = "SELECT *  from [DictionaryFile$] where words='"+match.ToString()+"'";
        //       OleDbCommand cmd = new OleDbCommand(querystr, objConn);
        //       OleDbDataAdapter oleda = new OleDbDataAdapter();
        //        oleda.SelectCommand = cmd;
        //        DataTable dt = new DataTable();
        //        oleda.Fill(dt);
        //        if(dt.Rows.Count>0)
        //        {
        //            Disc = dt.Rows[0]["Definition"].ToString().ToLower();
        //        }
        //        return Regex.Replace(match.ToString(), "\\b('" + Pipe + "')\\b", "<span id='" + match.ToString() + "' style='background-color: yellow; cursor: hand;' onmouseover='javascript:SaveMouseOVerWords(this.id)' onclick='javascript:FncAddedByAddon(this.title)'  title='" + Disc + "'>'" + match.ToString() + "'</span>");
        //    }
        //    return match.ToString();
        //}

        public void Save_OnMouseOver_WordS_A_Text_Details(string Word)
        {
            WordS_A_Text_Service obj = new WordS_A_Text_Service();
            int i = 0;
            i = obj.Save_OnMouseOver_WordS_A_Text_Details(RecordID, Word);
        }
        //------------------------------------------------------------------------------------------------------------
        #endregion
 
Any help appreciated.
AnswerRe: Image is not getting displayed after Addons Fire on IEmemberSharjith23 May '12 - 4:40 
Request you to please not post questions irrelevant to this article. There is a seperate Question/Answer forum for your questions. Please remove your message as it misleads the readers.
RegardsSmile | :)
N. Sharjith

GeneralMy vote of 5memberVolynsky Alex23 May '12 - 3:47 
It's really good job!!!
Thanks!!!
GeneralRe: My vote of 5memberSharjith23 May '12 - 3:56 
Thank you!
RegardsSmile | :)
N. Sharjith

QuestionGood job, please consider...memberKochise22 May '12 - 22:26 
Nice to see your own implementation. Have a look at alternative plugin frameworks, they might have solved this differently :
 
http://www.c-pluff.org/
http://www.drdobbs.com/cpp/206503957 or http://philippe.ameline.free.fr/techytechy/071125_PluginFramework.htm (first 4 parts only)
 
I remember having seen a now deprecated plugin framework in C that provided several elegant solutions, I have a copy on my hard disk, if that's of any interrest.
 
Kochise
 
In Code we trust !
AnswerRe: Good job, please consider...memberSharjith23 May '12 - 1:25 
Thanks for the links! As you pointed out, this implementation is truly my own research. All frameworks and methods that I had come across were either using C++ interface based programming or using COM. It is quite difficult to understand both the sides (host and plugin) unless we know the complete mechanism. I remember seeing few C headers in the Netscape installation folder, long back, which had similar function declarations expected to be provided an implementation for by the plugin developer. At that time I never had a clue how the application really used the shared objects and how the shared objects communicated to the plugin as well. Finally I found out after a lot of research and guess work how to implement and present a solution. Please do share the code that you have too. Though they may be deprecated they can possibly provide a good understanding for an intermediate level programmer. Thanks once again.
RegardsSmile | :)
N. Sharjith

GeneralMy vote of 5membermatthewlink7722 May '12 - 12:39 
Good article. My 5. Just to let you know one of the image is not showing.
GeneralRe: My vote of 5memberSharjith22 May '12 - 14:18 
Thanks for the vote. I guess it was the last image. Probably you may have accessed the article while I was in the middle of updating it. Now it shows the dialog screenshot taken on Win7.
RegardsSmile | :)
N. Sharjith

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

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130523.1 | Last Updated 25 May 2012
Article Copyright 2012 by Sharjith
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid