正如标题所暗示的那样,我的第一种插件架构方法遇到了麻烦,我希望得到一些建议,但让我具体说一下……
假设您有一个带有一些定义方法的纯抽象类 BASE... 假设您还有一个主要的可执行文件,以及一个在运行时与主要可执行文件动态链接的 dll 文件...BASE 位于包含在主文件中的头文件中exe 和 dll... BASE 子类在 dll 中定义,并实现了它们的抽象方法... Main exe 分配了一个指向 BASE 的指针数组。该数组被传递给 dll。dll 在堆上创建对象,并返回填充有指向这些对象实例的指针的数组。
问题是,当我尝试在主 exe 上的那些实例之一上调用 BASE 的成员方法时(应该由派生类在 dll 中重载),我收到访问冲突错误。我没有通过我为主要可执行文件和 dll 定义的 c 接口传递类或任何特定于 c++ 的东西......只是指针......
这实际上只是出于学术目的,但我仍然在考虑可能不同的人会为主程序创建插件(dll)的想法,因此兼容性是一个问题。所以我认为将 dll 的接口保留在 c 中是个好主意,您不同意吗?
我认为 dll 和主可执行文件共享相同的堆空间,所以指向对象实例的指针应该可以工作,不是吗?
另一方面,指向对象的指针只会给主程序一个内存中存储实例变量的位置,但没有关于其方法代码的信息,主程序对此一无所知,因为它是在 dll 中定义的. 所以如果没有可执行代码的地址,它就不能调用抽象类的方法实现是合乎逻辑的。到目前为止,我的推测是否正确?
我知道我可以使用保存在每个对象中的函数指针,但这不会破坏 C++ 好的内置多态机制吗?
另一种方法是,因为 BASE 的抽象方法是已知的,所以为每个 BASE 类的方法实现一个 c 风格的函数(在 dll 的 c 接口中),并带有一个额外的参数,即对象的 ptr。通过这种方式,可以在特定对象的 dll 内处理实现的方法。我非常不喜欢这个主意。使dll的接口更大,使用了大量的c函数,几乎使派生类的处理成为c风格,或者至少需要包装在其他c++类中。你对此有什么想法?
我主要问社区的是:我尝试这样做的方式是正确的方法吗?还是我的想法完全错误?我错过了什么重要的东西吗?你还有什么建议?
非常感谢您的时间和支持。
更新: 由于我无法弄清楚我做错了什么,我将求助于上传我的代码......
主程序 - .cpp 文件
#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 );
}
Run Code Online (Sandbox Code Playgroud)
以下是 dll 文件的 .cpp 文件...
#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;
}
Run Code Online (Sandbox Code Playgroud)
最后但并非最不重要的是主程序和 dll 中的头文件...
#ifndef FEATS_H
#define FEATS_H
#include <string>
namespace feats {
class feat {
public:
virtual std::string getName(void) = 0;
};
}
#endif
Run Code Online (Sandbox Code Playgroud)
因此,在提出这个问题时,我希望我的问题更清楚......我试图让它尽可能紧凑......所以,再次感谢你的帮助......
更新 2 - 解决方案 上面的代码似乎是正确的并且可以正常工作......我的问题是,在 Visual Studio 上工作是我在发布模式下编译了 dll,而主程序在调试模式下。翻转解决了问题......这么简单的错误,引起了这样的沉思......无论如何,我将上面的代码作为未来搜索的示例......
只需在头文件中定义抽象接口:
class AbstractBase
{
public:
virtual int SomeFunction() = 0;
virtual int SomeOtherFunction() = 0;
};
Run Code Online (Sandbox Code Playgroud)
在该头文件中还定义了两个 C 函数来创建和销毁一个类:
AbstractBase *CreateClass();
void DestroyClass(AbstractBase *data);
Run Code Online (Sandbox Code Playgroud)
在你的 dll 中实现具体的类:
class MyClass: AbstractBase
{
public:
SomeFunction() {};
virtual ~SomeFunction() {};
virtual int SomeFunction() { return 1; }
virtual int SomeOtherFunction() { return 1; }
};
Run Code Online (Sandbox Code Playgroud)
在你的 dll 中还实现了 create 和 destroy 函数:
AbstractBase *CreateClass()
{
return new SomeFunction();
}
void DestroyClass(AbstractBase *data);
{
delete data;
}
Run Code Online (Sandbox Code Playgroud)
现在在主要的可执行文件加载中,在运行时创建一个 destroy dll 入口点。
调用 create 入口点以获取对象并使用抽象接口与该对象对话。
调用 destroy 入口点销毁对象。
归档时间: |
|
查看次数: |
1229 次 |
最近记录: |