自行设计NPAPI开发框架 (二)

2014-11-24 08:43:30 · 作者: · 浏览: 1
inFuncs结构的newp指针就是在调用我们实现的NPP_New。
再来看看NPN_函数的实现,以NPN_GetValue为例,代码如下:
[cpp]
NPError NPN_GetValue(NPP instance, NPNVariable variable, void* value)
{
return sBrowserFuncs->getvalue(instance, variable, value);
}
NPError NPN_GetValue(NPP instance, NPNVariable variable, void* value)
{
return sBrowserFuncs->getvalue(instance, variable, value);
}
直接调用了NPNetscapeFuncs结构中的相应函数,因此我们调用NPN_GetValue其实就是在调用NPNetscapeFuncs结构中的getvalue。
其实将这些NPP_和NPN_的函数结构NPPluginFuncs和NPNetscapeFuncs联系起来的工作都是几乎一样的,于是就有了NPAPI插件开发的各种框架,这些框架的最基本作用就是干这事儿。有了框架我们开发插件就可以集中精力在实现这些NPP开头的函数的功能上。
以此为指导思想,写出一个插件,只有一个头文件和一个cpp文件(当然还有rc文件和def文件),编译生成dll在Firefox中about:plugins页面可以看到我们的插件。该代码请参考本文提供的附件。我将其命名为aemo,就当是alpha版的demo吧! 下载地址:http://download.csdn.net/detail/z6482/4913874
面向对象开发插件
相信绝大多数插件开发者都是选择C++来开发插件的吧,利用C++面向对象对插件开发进行一定的封装,可以让我们在开发插件的过程中更加专注于插件的实际功能。为了达到这个目的,我们最直接的想法就是将前面提到的NPP_开头的函数封装到一个类中,当我们开发插件的时候就只需要根据我们实际的功能需求来实现类中的相应函数就可以了。来看看sdk中是如何做的:
[cpp]
NPError NPP_NewStream(NPP instance, NPMIMEType type, NPStream* stream, NPBool seekable, uint16_t* stype)
{
if (!instance)
return NPERR_INVALID_INSTANCE_ERROR;
nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata;
if (!plugin)
return NPERR_GENERIC_ERROR;
return plugin->NewStream(type, stream, seekable, stype);
}
NPError NPP_NewStream(NPP instance, NPMIMEType type, NPStream* stream, NPBool seekable, uint16_t* stype)
{
if (!instance)
return NPERR_INVALID_INSTANCE_ERROR;
www.2cto.com
nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata;
if (!plugin)
return NPERR_GENERIC_ERROR;
return plugin->NewStream(type, stream, seekable, stype);
}
函数代码比较简单,功能就是将instance的pdata转换为类,然后调用该类的NewStream成员函数。因此我们要实现NPP_NewStream的功能,就只需要实现nsPluginInstanceBase类的NewStream函数的功能即可。简要叙述一下如何将instance的pdata与nsPluginInstanceBase联系起来。请看NPP_New中的代码片段:
[cpp]
nsPluginInstanceBase * plugin = NS_NewPluginInstance(&ds);
if (!plugin)
return NPERR_OUT_OF_MEMORY_ERROR;
instance->pdata = (void *)plugin;
nsPluginInstanceBase * plugin = NS_NewPluginInstance(&ds);
if (!plugin)
return NPERR_OUT_OF_MEMORY_ERROR;
instance->pdata = (void *)plugin;
这里NS_NewPluginInstance创建了一个nsPluginInstanceBase类的对象(准确的说是其子类的对象),然后将这个对象指针转换为void类型的指针,赋值给instance的pdata。这样就建立了instance与我们创建的对象之间的关系。完成了上述操作,就完成了NPP_函数的封装,
这里还有一点非常美妙的东西需要指出,instance是一个NPP结构,NS_NewPluginInstance中,用这个instance创建了一个插件实例对象,即该对象包含了一个指向instance的指针,而后面instance的pdata又指向了我们创建的这个对象,这样就达到了既可以通过instance访问plugin对象又可以通过plugin对象访问instance的目的。可能你觉得这没什么稀罕的,但是如果在开发插件的过程中需要创建多个类。而这些类又需要与plugin对象共享一些数据,那么就可以为这些类都添加一个NPP的成员,指向这个instance,然后,共享数据就变得非常有意思了。当然你也可以用C++固有的数据共享的方式(如:友元等)来共享数据,但我更喜欢这种方式!
nsPluginInstanceBase是一个插件实例的基类,该基类定义了几个纯虚函数,我们在创建插件的时候只需要以该类为基类派生一个子类并实现这几个定义为纯虚函数的函数即可生成一个最简单的插件,如果有其他需求则可以根据实际功能对其他虚函数进行覆写即可。如果没有这个基类的其他函数的实现那么我们每次写的时候都要将所有与NPP有关的函数都进行实现,即使没有功能,也至少需要一个空函数体,比如:一般NPP_Print这个打印功能,我们一般是不需要的。如果没有nsPluginInstanceBase,那么每次我们都要写一段这样的代码void NPP_Print(NPPrint* print