Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

c++ dll windows plugin architecture approach

Tags:

c++

plugins

dll

as the title suggests, I'm having trouble with my first approach to plugin architecture and I hope for some advice, but let me be specific...

Suppose you have a pure abstract class BASE with some defined methods... Suppose also that you have a main executable, and a dll file dynamically linked at runtime with the main executable... BASE is in a header file included in both the main exe and the dll... BASE subclasses are defined in the dll, and their abstract methods implemented... Main exe allocates an array of pointers to BASE. That array is passed to the dll. The dll creates objects on the heap, and returns the array filled with pointers to instances of those objects.

The problem is, that when I try to call a member method of BASE on one of those instances on the main exe, (which should be overloaded in the dll by the derived class), I get an access violation error. I'm not passing classes or anything c++ specific through the c interface I have defined for the main executable and the dll... Just pointers...

This is just for academic purposes really, but still I'm toying with the idea that perhaps different people would create plug ins (dlls) for the main program, so compatibility is an issue. So I think it is a good idea to keep the dll's interface in c, wouldn't you agree?

I think the dll and the main executable share the same heap space, so the pointers to the object instances should work, isn't that correct?

On the other hand, a pointer to an object would just give the main program a place in memory where the instance's variables are stored, but no information about its methods' code, which the main program knows nothing about, since it's defined in the dll. So it's logical that without the address of the executable code, it can't call the abstract classes' method implementation. Is my speculation correct thus far?

I know I could use function pointers kept inside each object, but wouldn't that destroy the nice built-in polymorphic mechanism of c++?

Another approach would be, since the abstract methods of BASE are known, to implement a c-style function (in the dll's c interface), for each BASE class' methods, with an extra argument, the object's ptr. This way the implemented method could be handled inside the dll on the specific object. I don't like this idea very much. Makes the dll's interface bigger and a lot of c functions are used, almost makes the handling of the derived classes c-style, or at least in need of wrapping inside other c++ classes. What are your thoughts on it?

What I'm basically asking the community is: Is the way I'm trying to do it the correct approach? or are my thoughts completely wrong? Am I missing out something important? What else would you suggest?

Thank you very much for your time and support.

UPDATE: Since I can't in no way figure out what I'm doing wrong, I will resort to uploading my code...

Main Program - .cpp file

#include "feats.h"

using namespace std;
typedef void* (__cdecl *_connect)(void);

void addFeats(vector<feats::feat*> &vec, void* pluginDllRet) {
    size_t i = 0;
    feats::feat **retVal = (feats::feat**) pluginDllRet;
    while(retVal[i] != NULL) vec.push_back(retVal[i++]);
}

int main(void) {
    HINSTANCE dllFile = LoadLibrary("dllName.dll");
    if(dllFile == NULL) { /*error Handling*/ }

    _connect con = (_connect) GetProcAddress(dllFile, "_connect");
    if(con == NULL) { /*error Handling*/ }

    vector<feats::feat*> featsHolder;
    addFeats(featsHolder, con());

    for(size_t i = 0; i < featsHolder.size(); i++)
        cout << featsHolder[i]->getName() << "\n";
    FreeLibrary(dllFile );
}

Following is the dll file's .cpp file...

#include "feats.h"
using namespace feats;
using namespace std;

extern "C" {
    __declspec(dllexport) void* __cdecl _connect(void) {
        return executableCode();    //<- this is visible in main program
    }
}

namespace feats {
    class acrobatic : public feats::feat {
        public:
        std::string getName(void) {
            return "Acrobatic";
        }
    };

    class powerfull : public feats::feat {
        public:
        std::string getName(void) {
            return "Powerfull";
        }
    };
}

void* executableCode(void) {
    feats::feat **fts = new feats::feat*[3];
    fts[0] = new acrobatic;
    fts[1] = new powerfull;
    fts[2] = NULL;
    return (void*) fts;
}

And last but not least the header in both main program and the dll...

#ifndef FEATS_H
#define FEATS_H

#include <string>

namespace feats {
    class feat {
        public:
        virtual std::string getName(void) = 0;
    };
}
#endif

So with this presented I hope my problem is more clear... I tried to make it as compact as possible... So, thanks again for your help...

UPDATE 2 - Solution As it seems the above code is correct and working... My problem, working on visual studio was that I compiled the dll in release mode, while the main program was in debug mode. Flipping that fixed the problem... Such a simple error, causing such contemplation... Anyway, I'm leaving the above code as an example for future searches...

like image 445
Frustrated Soul Avatar asked Apr 28 '16 05:04

Frustrated Soul


1 Answers

Just define you abstract interface in a header file:

class AbstractBase
{
public:
  virtual int SomeFunction() = 0;

  virtual int SomeOtherFunction() = 0;
};

In that header also define two C functions to create and destory a class:

AbstractBase *CreateClass();    

void DestroyClass(AbstractBase *data);    

Inside you dll implement the concrete class:

class MyClass: AbstractBase
{
public:
    SomeFunction() {};
    virtual ~SomeFunction() {};

    virtual int SomeFunction() { return 1; }

    virtual int SomeOtherFunction() { return 1; }
};

Inside your dll also implement the create and destroy functions:

AbstractBase *CreateClass()
{
    return new SomeFunction();
}

void DestroyClass(AbstractBase *data);    
{
   delete data;
}

Now inside the main executable load the create an destroy dll entry points at run time.

Call the create entry point to get an object and talk to that object using the abstract interface.

Call the destroy entry point to destroy the object.

like image 68
jussij Avatar answered Oct 20 '22 14:10

jussij