Implementing Plug-in System Using C++

    The plug-in mechanism can easily extend the functionality of existing applications. The basic idea of ​​implementing the plug-in mechanism with C++ is: the application program provides interfaces, the user or a third party implements these interfaces, and compiles the corresponding dynamic link library (that is, the plug-in); all plug-ins are placed in a specific directory, and the application runs This directory is automatically searched and the plugins in the directory are loaded dynamically.

Application provides interface

   In order to achieve functional extension, the application must provide an interface to the plug-in. Define an abstract class Base as an interface in base.h:

#ifndef BASE_H_
#define BASE_H_

class Base {
public:
    virtual ~Base() = default;
    virtual void print(void) = 0;
    virtual double calc(double val) = 0;
};

#endif

Implement plugin

   Plugins should contain and implement the interfaces provided by the application. Define Test1 in test1.h, let Test1 inherit and implement all the interfaces provided in Base:

#ifndef TEST1_H_
#define TEST1_H_

#include <iostream>
#include <cmath>
#include "main.h"

class Test1 : public Base {
public:
    void print(void) {
        std::cout << "Hello Everybody! Test1!" << std::endl;
    }

    double calc(double val) {
        return sqrt(abs(val / 5 * 1.61));
    }
};

#endif

   In order for the application to dynamically load the plugin, the plugin needs to be compiled into a dll file. In main.h, the plugin declares two exported functions: 

   - getObj: used to create a new Test1 object and return the pointer of the object; 

   - getName: used to print Test1 related information.

#ifndef __MAIN_HPP_INCLUDED__
#define __MAIN_HPP_INCLUDED__

#define BUILD_DLL

#include <memory>
#include <string>
#include "base.h"

#ifdef BUILD_DLL
    #define DLLAPI __declspec(dllexport)
#else
    #define DLLAPI
#endif // BUILD_DLL

// DLL export function

#ifdef __cplusplus
extern "C" {
#endif

    DLLAPI Base *getObj(void);
    DLLAPI const char* getName(void);

#ifdef __cplusplus
}
#endif

#endif // __MAIN_HPP_INCLUDED__

   In main.cpp, define getObj and getName and the DLL entry DLLMain function:

#include <iostream>
#include <cmath>
#include <windows.h>
#include "main.h"
#include "test1.h"

extern "C" Base* getObj(void) {
    return new Test1;
}

extern "C" const char* getName(void) {
    return "Test1:Maths";
}

extern "C" DLLAPI BOOL APIENTRY DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
    switch (fdwReason)
    {
        case DLL_PROCESS_ATTACH:
            // attach to process
            // return FALSE to fail DLL load
            break;

        case DLL_PROCESS_DETACH:
            // detach from process
            break;

        case DLL_THREAD_ATTACH:
            // attach to thread
            break;

        case DLL_THREAD_DETACH:
            // detach from thread
            break;
    }
    return TRUE; // succesful
}

    So far, a plugin has been implemented. Multiple dll plugins can be implemented in this way.

implement the application

   Now let's write a simple application, the function is to load all the dll plugins in the plugins directory, print out the dll related information, and call the functions implemented in the plugins.

   First, implement an own exception class in my_exception.h to capture exception messages of type wstring:

#ifndef MY_EXCEPTION_H_
#define MY_EXCEPTION_H_

#include <string>
#include <stdexcept>

class MyException : public std::runtime_error {
public:
    MyException(const std::wstring &msg)
        : runtime_error("Error"), message_(msg) {
    }
    ~MyException() throw() {}
    std::wstring message() { return message_;}
private:
    std::wstring message_;
};

#endif

    Then, implement the relevant functions of the application in main.cpp:

#include <iostream>
#include <vector>
#include <memory>
#include <stdexcept>
#include <exception>
#include <windows.h>

#include "my_exception.h"
#include "base.h"

// Function: Load all dll plugins in the plugins directory
// Parameters: modules are used to save all dll file handles, all handles will finally be released by the FreeLibrary() function in the main function
// return:
std::vector<Base*> getPlugins(std::vector<HINSTANCE>& modules) {
    std::vector<Base*> ret;
    modules.clear();

    // Find the dll file in the plugins directory and save the file information in fileData
    WIN32_FIND_DATA fileData;
    HANDLE fileHandle = FindFirstFile(L"plugins/*.dll", &fileData);

    if (fileHandle == (void*)ERROR_INVALID_HANDLE ||
        fileHandle == (void*)ERROR_FILE_NOT_FOUND) {
        // no dll file found, return empty vector
        return std::vector<Base*>();
    }

    // Loop through all the dll files in the plugins directory
    do {
        typedef Base* (__cdecl *ObjProc)(void);
        typedef const char* (__cdecl *NameProc)(void);

        // Load the dll into the address space of the current process
        HINSTANCE mod = LoadLibrary((L"./plugins/" + std::wstring(fileData.cFileName)).c_str());

        if (!mod) {
            // If loading dll fails, release all loaded dlls
            for (HINSTANCE hInst : modules)
                FreeLibrary(hInst);
            throw MyException(L"Library " + std::wstring(fileData.cFileName) + L" wasn't loaded successfully!");
        }
        // Get the function addresses of getObj and getName from the dll handle
        ObjProc objFunc = (ObjProc) GetProcAddress(mod, "getObj");
        NameProc nameFunc = (NameProc) GetProcAddress(mod, "getName");

        if (!objFunc || !nameFunc)
            throw std::runtime_error("Invalid Plugin DLL: both 'getObj' and 'getName' must be defined.");

        ret.push_back(objFunc()); // Save the object pointer generated by objFunc (ie getObj)
        modules.push_back(mod); // save the dll handle

        std::clog << nameFunc() << " loaded!\n";
    } while (FindNextFile(fileHandle, &fileData));

    std::clog << std::endl;

    // close the file handle
    FindClose(fileHandle);
    return ret;
}

int main() {
    std::vector<HINSTANCE> modules;

    {
        std::vector<Base*> objs;

        // load plugin
        try {
            objs = getPlugins(modules);
        } catch (const std::exception& e) {
            for (auto& x : objs) {
                delete x;
            }
            std::cerr << "Exception caught: " << e.what() << std::endl;
            return 1;
        }

        // Call the implementation of the Base interface in the plugin
        for (auto& x : objs) {
            x->print();
            std::cout << "\t" << x->calc(10) << std::endl;
        }
        for (auto& x : objs) {
            delete x;
        }
    }

    // free all dlls
    for (HINSTANCE hInst : modules)
        FreeLibrary(hInst);


    return 0;
}

run

   Compile all plug-ins into dll files and put them in the plugins directory under the current project directory, start the application, and the plug-ins will be automatically loaded into the program. The result is shown in the following figure: 


Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325642850&siteId=291194637