通常,动态库链接我们都是在编译时候就指定链接位置和链接库的名字。这个方式叫做隐式调用,最简单也是通常我们经常用到的一个方式。
但是往往 ,我们在做支持扩展插件so或者其他特殊应用的时候,会用到Linux提供的加载动态链接库的api ,就是dlopen dlsym dlclose dlerror等。
使用api 我们能够无需事先知道链接库的名字,只有程序在运行要用到动态链接库的时候,就去指定的插件目录查找存在的so文件,然后加载就好,只要提供插件的函数名和我们预期一样,那么就能够准确调用并运行得到自己想要的结果。这个方法在c 语言编译生成的动态链接库使用起来一点问题都没有
但是,在c++编译生成的动态链接库就不一样了,一个是c++支持函数重载,动态库中的名字是加入了标志着函数参数信息的magic字符。这个会导致我们在dlsym的时候,根本无法获取到对应的我们命名的函数。好在只要我们只要对函数符号进行查找,一样是能够解决这个问题的。 最最麻烦的问题是使用到了动态链接库的类,还有类的成员函数,我们在程序中要实例化类,对类的函数进行调用,拿到类的成员等等,到这一步就非常难搞了.....
有没有大佬能够指点一下,怎么才能够解决这个问题?
注:目前最麻烦的是插件的动态库不是自己编译的,无法通过加入一些函数来在动态库内部实例化类返回指针这种形式解决
最近编辑记录 微凉VeiLiang (2021-11-16 17:17:14)
离线
Qt有动态库so,里面都是Qt类;它是如何实现的呢?是不是需要一个导入库,像Windows的gdi++那样?
我估计dlopen系列api只能用于C,用于C++不合适;C++对象不只有方法,还有对象内存块本身,还涉及到构造器和析构器和重载,太复杂。
你在C中引用了它,却无法调用new和delete操作,对象何去何从呢?是不是一堆bug等着你呢?
最近编辑记录 armstrong (2021-11-16 16:33:54)
离线
别人编译好的C++动态库,应该要别人提供的导入库和头文件才行。
离线
大家都知道gdi+是用于C++开发的,我看了一下mingw下的gdi+,它是在一堆头文件里重新定义了C++对象,而对象的构造函数和析构函数分别调用相关资源的create和delete函数。也就是说,gdi+的dll导出的标号仍然是C标号,声明在gdiplusflat.h文件里,而非C++。只是靠头文件封装成了C++类而已。
最近编辑记录 armstrong (2021-11-16 16:51:18)
离线
@armstrong
是最适合c。C++不知道linux后面会不会加入这种方面的支持
有个做法就是在动态库里面提供对应的类的创建和销毁函数,然后动态库里面实例化和销毁。在创建时候返回的是类的地址,然后自己在外部定义一个同样的类,根据地址偏移进行操作(不知道是否准确,有点c语言结构体的思维了,类的对象地址排布好像也是一样的吧)
现在由于不能修改dll,好像这个办法就行不通了
离线
@armstrong
感谢,我去学习下看看。不过好像和上面说的方式类似。windows下dll不知道有没有类似问题的解决方案
离线
一样可以,类似于组件(plugin)加载,明天附上代码
离线
先引用原有的头文件,用C++封装一层,然后导出C接口。
离线
一样可以,类似于组件(plugin)加载,明天附上代码
感谢,蹲代码(:
离线
先引用原有的头文件,用C++封装一层,然后导出C接口。
有参考的实例么
离线
C++的ABI本来就是百家争鸣,每个编译器厂商都有自己的ABI,其中就有重载重命名(mangle)规则,甚至版本间都会改变。非要动态加载就把你可能用到的编译器和版本的mangle方法都写一遍,然后去撞,撞上了就用这个方法解码dlsym返回的symbol,撞不上就认定是无效插件。
离线
void CPluginManager::loadAll() {
//进入插件目录
QDir path = QDir(qApp->applicationDirPath());
path.cd("plugins");
//初始化插件中的元数据
foreach(QFileInfo info, path.entryInfoList(QDir::Files | QDir::NoDotAndDotDot)) {
scan(info.absoluteFilePath());
}
//加载插件
foreach(QFileInfo info, path.entryInfoList(QDir::Files | QDir::NoDotAndDotDot)) {
load(info.absoluteFilePath());
}
}
void CPluginManager::load(const QString &path) {
if (!QLibrary::isLibrary(path)) {
return;
}
if (!d->check(path)) {
return;
}
auto loader = new QPluginLoader(path);
if (loader->load()) {
//如果继承自我们的 plugin接口,则认为是自己的插件,防止外部插件注入
CPlugin *plugin = qobject_cast<CPlugin *>(loader->instance());
if (plugin) {
d->m_loaders.insert(path, loader);
} else {
delete loader;
loader = Q_NULLPTR;
}
}
}
离线
@cgpsky
看上去好像还是要求dll有除了构造函数之外的,能够返回实例化好类地址的成员函数。
离线