🎉 给自己的程序写一个插件 🎉

给自己的程序写一个插件

插件!我相信大家都不陌生吧,但是大家有没有想过插件是怎么做的呢?为什么程序已经打包发布了,但加入插件又可以动态增加新的功能。比如wirehark这款网络抓包软件,我们就可以编写插件,解析自定义的协议。

为自己的程序编写插件步骤:

编写主要的应用程序。

编写插件程序。

将插件打包成动态库。

将动态库拷贝到主要应用程序指定目录。

重新运行并加载动态库即可实现加载插件。

通过上述的步骤,其实已经说明了制作插件的关键点,即动态库。接下来我们一步一步地写程序实现插件机制,了解制作插件的过程。

第一步:先确定接口,即主程序预留给插件接口,其实就是开放的函数接口。

#ifndef __INTERFACE_H__

#define __INTERFACE_H__

typedef struct plugin_info_t

{

int id;

char name[32];

void (*show)(struct plugin_info_t *info); //指针函数,显示插件中的信息

void (*operation)(struct plugin_info_t *info);//插件操作指针函数

}plugin_info_t;

plugin_info_t* register_plugins_func(void); //注册插件地函数

#endif // !__INTERFACE_H__

第二步:编写主程序,即加载插件的。

#include "interface.h"

#include

#include

#include

#include

#include

#include

#define MAX_PLUGIN_NUM 10 //最大允许加载外部插件的个数,可修改

#define PLUGINS_PATH "./lib" //插件存放路径

plugin_info_t *ginfo[MAX_PLUGIN_NUM]; //指针数组,用于存储加载的插件信息

void* dlhandler[MAX_PLUGIN_NUM]; //存放加载动态库的句柄

typedef plugin_info_t* (*register_func)(void); //注册函数指针

/**

* @brief:遍历lib目录下的so文件,加载插件

* @input: NONE

* @output: OK:加载的插件个数 ERR:0

*/

int load_plugins(void)

{

DIR *dir=NULL;

struct dirent *ptr=NULL;

char base[128]={0};

const char *path=PLUGINS_PATH; //插件加载路径

int plugin_cnt=0; //记录以及加载的插件个数

if ((dir=opendir(path)) == NULL) {

perror("Open dir error...");

return 0;

}

while ((ptr=readdir(dir)) != NULL)

{

if(strcmp(ptr->d_name,".")==0 || strcmp(ptr->d_name,"..")==0) //当前路径或父路径

continue;

else if(ptr->d_type == 8){

sprintf(base, "%s/%s", path,ptr->d_name); //获取动态库名字

dlhandler[plugin_cnt]=dlopen(base, RTLD_LAZY); //打开动态库

if(!dlhandler[plugin_cnt]){

perror("Load so error!\n");

continue;

}

register_func rfunc = dlsym(dlhandler[plugin_cnt], "register_plugins_func"); //找到动态库中的函数符合

if(!rfunc){

perror("Load Plugin Error!");

continue;

}

ginfo[plugin_cnt] = rfunc(); //获取插件中的信息,存入结构体数组

++plugin_cnt;

if(plugin_cnt >= MAX_PLUGIN_NUM) return 0; //判断是否超出最大的插件个数

}

}

closedir(dir);

return plugin_cnt;

}

void release_plugin(int num)

{

int i=0;

for(i=0; i

dlclose(dlhandler[i]); //关闭加载插件的句柄

}

}

int main(void)

{

int i=0;

int plugin_num=0;

plugin_num = load_plugins(); //加载插件

if(plugin_num==0){

printf("Load Plugins Error!!!\n");

return -1;

}

for(i=0; i

ginfo[i]->show(ginfo[i]); //结构体数组保存插件的信息,调用结构体中的函数指针

ginfo[i]->operation(ginfo[i]); //调用结构体中的函数指针

printf("\n"); //多一空行,方便显示

}

release_plugin(plugin_num); //释放资源

return 0;

}

第三步:编写插件,因篇幅原因,只展示一个插件,其他两个插件与下面这个几乎一样的,我们也可以根据自己需求修改添加插件。

#include

#include

#include "interface.h"

void demo1_show(struct plugin_info_t *info) //指针函数,显示插件中地信息

{

printf("Demo1 Plugin Info: ID=%d Name=%s \n", info->id, info->name);

}

void demo1_operation(struct plugin_info_t *info) //插件操作指针函数

{

//可自行发挥,写入想在插件中实现的功能

//此处演示,即打印一下信息

printf("Demo1 Plugin operation function...\n");

}

plugin_info_t* register_plugins_func(void) //在插件中实现注册函数

{

plugin_info_t* info = (plugin_info_t*)malloc(sizeof(plugin_info_t)); //申请内存

if(!info){

perror("Demo1 Plugin Error!\n");

return NULL;

}

info->id = 1;

sprintf(info->name, "Demo1 Plugin");

info->show = demo1_show; //给指针函数赋值

info->operation = demo1_operation; //给指针函数赋值

printf("注册插件[demo1] 完成...\n");

return info;

}

至此代码部分基本完成。PS:主程序中存储结构体信息采用的是数组,学有余力的小伙伴,可以尝试编写链表进行存储,使其更规范。

代码目录简介:此代码是Linux平台下运行的

plugin_code目录:包含所有的代码

app目录:存放的是主程序,即加载插件的。在此目录下还有一个lib目录,该目录是存放插件代码的动态库的,即so文件。

plugins目录:该目录存放插件代码,即plugin_demo1.c plugin_demo2.c、plugin_demo3.c三个插件。

编译运行:

编译主程序: 进入到plugin_code/app目录下,输入make命令,即可编译,生成的mainApp就是可执行文件。使用./mainApp运行。

编译插件:进入到plugin_code/plugin目录下,输入make命令编译插件。编译信息如下图:

运行程序效果:在plugin_code/app目录下输入 ./mainApp即可运行,下图输出的信息均来自3个插件输出的。

想要整个工程文件的小伙伴:请在微信公众号【Linux编程用C】回复 plugin

我是小C,欢迎大家关注、点赞支持,我们一起交流讨论学习!

✨ 相关推荐 ✨

gtx970显卡相当于什么水平
365速发国际是黑平台吗

gtx970显卡相当于什么水平

🎯 08-09 👁️ 5224
QQ炫舞手游合魔法套装技巧是什么
365速发国际是黑平台吗

QQ炫舞手游合魔法套装技巧是什么

🎯 07-09 👁️ 3214
闲钱放哪里收益高又安全?闲钱新去处:揭秘高收益与安全性并存!