现在很多程序都支持插件,有利于程序功能的扩展.也方便用户为程序定制一些个性化的功能.
实现的方法有很多,主要目的是将插件与应用程序的接口实现好就可以了,至于插件格式,这里我选用的是DLL.
首先,要定义一个保存插件信息的结构:
typedef struct _Plugin_T
{
//
// 插件的名称
//
char szPluginName[256];
//
// DLL的实例句柄
//
HINSTANCE hPlugin;
//
// 插件中处理命令行的函数
//
void (*pfn_get_cmd_line)(char* cmdline);
} PLUGIN_T, *LPPLUGIN_T;
现在还需要一个容器结构将插件的信息组织起来,方便调用,如下:
struct _Plugin_Vector
{
PLUGIN_T Plugins[50];
//
// 插件计数
//
unsigned int nCount;
} g_pv;
这里为了演示,预设50个插件(如果你很喜欢STL的话,可以使用vector来作为容器,这样会看起来更Cool).
有了上面两个结构,load_plugin()就可以根据用户的输入来载入相应的插件.代码如下:
int load_plugin(char* szFileName)
{
//
// 取得dll的实例句柄
//
g_pv.Plugins[g_pv.nCount].hPlugin = LoadLibrary(szFileName);
if(g_pv.Plugins[g_pv.nCount].hPlugin == NULL)
{
fprintf(stderr, "LoadLibrary() failed. --err: %d\n", GetLastError());
return -1;
}
//
// 取得插件名.由于szFileName包含文件的扩展名(.dll),
// 所以要将扩展名去掉
//
char szPluginName[256];
char* p = 0;
strcpy(szPluginName, szFileName);
p = strchr(szPluginName, '.');
*p = '\0';
strcpy(g_pv.Plugins[g_pv.nCount].szPluginName, szPluginName);
//
// 取得插件中处理命令行的函数指针(插件的导出函数)
//
g_pv.Plugins[g_pv.nCount].pfn_get_cmd_line = (void (*)(char*))GetProcAddress(g_pv.Plugins[g_pv.nCount].hPlugin, "get_cmd_line");
printf("Plugin %s Load Ok!\n", g_pv.Plugins[g_pv.nCount].szPluginName);
g_pv.nCount++;
return 0;
}
这样就可以通过.load <dll name>来加载所需要的插件,如:.load hack.dll(需要带扩展名).主程序中由get_cmd_line()来负责对输入的命令进行处理,代码如下:
void get_cmd_line()
{
char cmdline[256];
printf("cdsh# ");
gets(cmdline);
while(stricmp(cmdline, ".quit"))
{
if( !strnicmp(cmdline, ".load", 5) )
{
char* p = strchr(cmdline, ' ');
if(p != NULL)
load_plugin(p+1);
else
show_loaded_plugin();
}
else
get_plugin_cmd_line(cmdline);
printf("\ncdsh# ");
gets(cmdline);
}
}
get_cmd_line()首先要检查输入是否是应用程序所能处理的命令,如果不是,将调用get_plugin_cmd_line()将输入传递给相应的插件进行处理.调用插件的命令行为<插件名> <命令行>,例如:hack .help 表示调用hack插件的.help命令.
get_plugin_cmd_line()的代码如下:
void get_plugin_cmd_line(char* cmdline)
{
char* pch_space;
char plugin_name[256];
char plugin_cmd_line[256];
pch_space = strchr(cmdline, ' ');
//
// 检查传递给插件的命令格式是否正确
//
if(pch_space == NULL) return;
int i = 0;
char* p = NULL;
//
// 取得调用插件的名称
//
for(i = 0, p = cmdline; p < pch_space; i++, p++)
plugin_name[i] = *p;
plugin_name[i] = '\0';
//
// 取得命令行
//
for(i = 0, p = pch_space + 1; *p != '\0'; i++, p++)
plugin_cmd_line[i] = *p;
plugin_cmd_line[i] = '\0';
//
// 找出要调用的插件,执行命令
//
for(i = 0; i < g_pv.nCount; i++)
{
if( !stricmp(g_pv.Plugins[i].szPluginName, plugin_name) ) break;
}
g_pv.Plugins[i].pfn_get_cmd_line(plugin_cmd_line);
}
现在载入插件与执行插件的操作都已经成功了,还剩下一步,由于调用LoadLibrary()来载入的插件,在程序结束时需要调用FreeLibrary()来释放资源,代码如下:
void free_plugin()
{
for(int i = 0; i < g_pv.nCount; i++)
{
FreeLibrary(g_pv.Plugins[i].hPlugin);
g_pv.Plugins[i].pfn_get_cmd_line = NULL;
memset(g_pv.Plugins[i].szPluginName, 0, 256);
}
}
最后来看一下插件的编写,只需要编写一个dll并且导出其中的get_cmd_line()便可以了,至于其他的功能,可以在处理命令行的过程中实现.作为实例,我做了一个hack.dll,代码如下:
#include <stdio.h>
#include <windows.h>
#define __dll_export extern "C" __declspec(dllexport)
__dll_export void get_cmd_line(char* cmdline)
{
if(!stricmp(cmdline, ".help"))
printf("[In Hack.dll]HackIt! uha...\n");
}
以上便是整个的实现思路,还剩余一些问题,比如在这个程序中,我没有处理多次加载同一个插件的情况.如果大家感兴趣可以自己解决一下.
其实对于插件的支持,使用面向对象的方法来解决问题的话,会比现在简单的多,构造一个插件类,重载其中的功能函数,之后通过对象的指针来进行调用...