1. 概述
Miranda 提供了一套非常灵活的插件机制, 使得用户可以很方便的通过增减插件来添加或去除一些扩展功能. 源码里有一个testplug project, 演示了实现一个Miranda插件的基本方法,下面就通过对testplug project的分析, 来学习Miranda的插件技术.
2. Miranda插件分类
基本插件: srmm.dll,clist_modern.dll,dbx_3x.dll
协议插件: jabber,msn,qq等
辅助插件: png2dib等
3. 一个插件需要实现的基本接口与变量
3.1.基本变量
这个通过分析testplug project可以很清楚的看到,一个插件有三个基本全局变量:
HINSTANCE hInst;
//记录该插件的模块句柄
PLUGINLINK *pluginLink; //用来登记通用的操作插件的函数指针
PLUGININFO pluginInfo; //该插件的描述信息
3.2.基本接口
BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved)
{
hInst=hinstDLL;
return TRUE;
}
//这是一个菜单命令的响应函数
static int PluginMenuCommand(WPARAM wParam,LPARAM lParam)
{
MessageBox(NULL,"Just groovy, baby!","Plugin-o-rama",MB_OK);
return 0;
}
__declspec(dllexport) PLUGININFO* MirandaPluginInfo(DWORD mirandaVersion)
{
return &pluginInfo;
}
int __declspec(dllexport) Load(PLUGINLINK *link)
{
pluginLink=link;
//下面这段代码在主菜单里插入菜单项 "&Test Plugin..."
//当单击该菜单项时PluginMenuCommand将被调用
CLISTMENUITEM mi;
CreateServiceFunction("TestPlug/MenuCommand",PluginMenuCommand);
ZeroMemory(&mi,sizeof(mi));
mi.cbSize=sizeof(mi);
mi.position=-0x7FFFFFFF;
mi.flags=0;
mi.hIcon=LoadSkinnedIcon(SKINICON_OTHER_MIRANDA);
mi.pszName="&Test Plugin...";
mi.pszService="TestPlug/MenuCommand";
CallService(MS_CLIST_ADDMAINMENUITEM,0,(LPARAM)&mi);
return 0;
}
int __declspec(dllexport) Unload(void)
{
return 0;
}
3.3.插件如何添加并响应自己的菜单命令
参考3.2 基本接口 中Load函数中加载菜单项 "&Test Plugin..."的方法.
4. 主要数据结构:
4.1.PLUGINLINK
typedef struct {
HANDLE (*CreateHookableEvent)(const char *);
int (*DestroyHookableEvent)(HANDLE);
int (*NotifyEventHooks)(HANDLE,WPARAM,LPARAM);
HANDLE (*HookEvent)(const char *,MIRANDAHOOK);
HANDLE (*HookEventMessage)(const char *,HWND,UINT);
int (*UnhookEvent)(HANDLE);
HANDLE (*CreateServiceFunction)(const char *,MIRANDASERVICE);
HANDLE (*CreateTransientServiceFunction)(const char *,MIRANDASERVICE);
int (*DestroyServiceFunction)(HANDLE);
int (*CallService)(const char *,WPARAM,LPARAM);
int (*ServiceExists)(const char *); //v0.1.0.1+
int (*CallServiceSync)(const char *,WPARAM,LPARAM); //v0.3.3+
int (*CallFunctionAsync) (void (__stdcall *)(void *), void *); //v0.3.4+
int (*SetHookDefaultForHookableEvent) (HANDLE, MIRANDAHOOK); // v0.3.4 (2004/09/15)
} PLUGINLINK;
extern PLUGINLINK *pluginLink;
#define CreateHookableEvent(a)
pluginLink->CreateHookableEvent(a)
#define DestroyHookableEvent(a)
pluginLink->DestroyHookableEvent(a)
#define NotifyEventHooks(a,b,c)
pluginLink->NotifyEventHooks(a,b,c)
#define HookEventMessage(a,b,c)
pluginLink->HookEventMessage(a,b,c)
#define HookEvent(a,b)
pluginLink->HookEvent(a,b)
#define UnhookEvent(a)
pluginLink->UnhookEvent(a)
#define CreateServiceFunction(a,b)
pluginLink->CreateServiceFunction(a,b)
#define CreateTransientServiceFunction(a,b) pluginLink->CreateTransientServiceFunction(a,b)
#define DestroyServiceFunction(a)
pluginLink->DestroyServiceFunction(a)
#define CallService(a,b,c)
pluginLink->CallService(a,b,c)
#define ServiceExists(a)
pluginLink->ServiceExists(a)
#define CallServiceSync(a,b,c)
pluginLink->CallServiceSync(a,b,c)
#define CallFunctionAsync(a,b)
pluginLink->CallFunctionAsync(a,b)
#define SetHookDefaultForHookableEvent(a,b) pluginLink->SetHookDefaultForHookableEvent(a,b)
4.2.pluginEntry
typedef struct pluginEntry {
char pluginname[64]; //插件的名称
unsigned int pclass; // PCLASS_*
BASIC_PLUGIN_INFO bpi;
struct pluginEntry * nextclass;
} pluginEntry;
4.3.PLUGININFO
typedef struct {
int cbSize;
char *shortName;
DWORD version;
char *description;
char *author;
char *authorEmail;
char *copyright;
char *homepage;
BYTE isTransient; //leave this as 0 for now
int replacesDefaultModule; //one of the DEFMOD_ constants in m_plugins.h or zero
//if non-zero, this will supress the loading of the specified built-in module
//with the implication that this plugin provides back-end-compatible features
} PLUGININFO;
4.4.BASIC_PLUGIN_INFO
typedef struct { // can all be NULL
HINSTANCE hInst;
Miranda_Plugin_Load Load;
Miranda_Plugin_Unload Unload;
Miranda_Plugin_Info Info;
Database_Plugin_Info DbInfo;
CList_Initialise clistlink;
PLUGININFO * pluginInfo; // must be freed if hInst==NULL then its a copy
DATABASELINK * dblink; // only valid during module being in memory
} BASIC_PLUGIN_INFO;
5. 主要全局变量:
SortedList pluginList = { 0 },
pluginListAddr = { 0 };
PLUGINLINK pluginCoreLink;
char
mirandabootini[MAX_PATH];
static DWORD mirandaVersion;
static pluginEntry * pluginListDb;
static pluginEntry * pluginListUI;
static pluginEntry * pluginList_png2dib = NULL;
static HANDLE hPluginListHeap = NULL;//插件用到的堆
static pluginEntry * pluginDefModList[DEFMOD_HIGHEST+1]; // do not free this memory
static int askAboutIgnoredPlugins;
6. 辅助函数:
3.1 HINSTANCE GetInstByAddress( void* codePtr )
根据一个函数的地址返回函数所在的模块的句柄
3.2 enumPlugins(scanPluginsDir, 0, 0);
枚举所有的插件
3.3 checkAPI
动态加载一个插件,并检查该插件的API函数
Load,Unload,MirandaPluginInfo 三个基本的API函数