基于NPAPI接口的windows平台安全控件开发(二)

    通过前一篇博客,已经实现了基本的功能和图形界面  。本篇主要介绍如何实现插件与JS交互。交互功能主要是通过NPObject对象来实现的。

   上面提到我们重写了NPP_GetValue函数,使NPP_GetValue去调用CPlugin中的GetScriptObject,而不是按默认流程调用GetValue(其实无所谓,改个名称而已)。所以,当插件加载完成后,浏览器会调用NPP_GetValue并将variable参数设置为NPPVpluginScriptableNPObject来查询浏览器是否支持与脚本语言的交互功能,如果插件支持交互功能,则NPP_GetValue会返回一个NPOject对象给浏览器,此时浏览器就可以通过这个NPObject对象与插件建立联系。所以在CPlugin::GetScriptObject函数中,我们要实现NPObject对象。实现如下:

NPObject *CPlugin::GetScriptableObject()
{
    if (!m_pScriptableObject) {  
    m_pScriptableObject = NPN_CreateObject(m_pNPInstance,GET_NPOBJECT_CLASS(CScriptObject));
    }  
    if (m_pScriptableObject) {  
    NPN_RetainObject(m_pScriptableObject);  
    }  
    return m_pScriptableObject; 
}

当存在NPObject对象时,直接使用NPN_RetainObject获取并返回。若不存在NPObject对象时,使用NPN_CreateObject创建一个NPObject对象。GET_NPOBJECT_CLASS宏定义如下:

#define DECLARE_NPOBJECT_CLASS_WITH_BASE(_class, ctor)                        \
static NPClass s##_class##_NPClass = {                                        \
NP_CLASS_STRUCT_VERSION_CTOR,                                             \
ctor,                                                                       \
ScriptablePluginObjectBase::_deallocate,                                    \
ScriptablePluginObjectBase::_invalidate,                                    \
ScriptablePluginObjectBase::_hasMethod,                                     \
ScriptablePluginObjectBase::_invoke,                                        \
ScriptablePluginObjectBase::_invokeDefault,                                 \
ScriptablePluginObjectBase::_hasProperty,                                   \
ScriptablePluginObjectBase::_getProperty,                                   \
ScriptablePluginObjectBase::_setProperty,                                   \
ScriptablePluginObjectBase::_removeProperty,                                \
ScriptablePluginObjectBase::_enumerate,                                     \
ScriptablePluginObjectBase::_construct                                      \
}

宏中的ScriptablePluginObjectBase定义如下:

class ScriptablePluginObjectBase : public NPObject
{
public:
ScriptablePluginObjectBase(NPP npp)
: mNpp(npp)
{
}
virtual ~ScriptablePluginObjectBase()
{
}
// Virtual NPObject hooks called through this base class. Override
// as you see fit.
//virtual NPObject *allocate(NPP npp, NPClass *aClass){}
virtual void invalidate();
virtual bool hasMethod(NPIdentifier name);
virtual bool invoke(NPIdentifier name, const NPVariant *args,uint32_t argCount, NPVariant *result);
virtual bool invokeDefault(const NPVariant *args, uint32_t argCount,NPVariant *result);
virtual bool hasProperty(NPIdentifier name);
virtual bool getProperty(NPIdentifier name, NPVariant *result);
virtual bool setProperty(NPIdentifier name, const NPVariant *value);
virtual bool removeProperty(NPIdentifier name);
virtual bool enumerate(NPIdentifier **identifier, uint32_t *count);
virtual bool construct(const NPVariant *args, uint32_t argCount,NPVariant *result);
public:
static NPObject *_allocate(NPP npp, NPClass *aClass);
static void _deallocate(NPObject *npobj);
static void _invalidate(NPObject *npobj);
static bool _hasMethod(NPObject *npobj, NPIdentifier name);
static bool _invoke(NPObject *npobj, NPIdentifier name,const NPVariant *args, uint32_t argCount,NPVariant *result);
static bool _invokeDefault(NPObject *npobj, const NPVariant *args,uint32_t argCount, NPVariant *result);
static bool _hasProperty(NPObject * npobj, NPIdentifier name);
static bool _getProperty(NPObject *npobj, NPIdentifier name,NPVariant *result);
static bool _setProperty(NPObject *npobj, NPIdentifier name,const NPVariant *value);
static bool _removeProperty(NPObject *npobj, NPIdentifier name);
static bool _enumerate(NPObject *npobj, NPIdentifier **identifier,uint32_t *count);
static bool _construct(NPObject *npobj, const NPVariant *args,uint32_t argCount, NPVariant *result);
static NPObject *AllocateCScriptObject(NPP npp, NPClass *aClass);
protected:
NPP mNpp;
};

该类需要继承自NPObject,因为插件需要给浏览器返回一个NPObject对象。随后再定一个类CScriptObject。

class CScriptObject:
public ScriptablePluginObjectBase
{
public:
NPUTF8 *utf8_chars1,*utf8_chars2,*utf8_chars3,*utf8_chars4;//缓冲区
static NPObject *sWindowObj;
static NPIdentifier s_idGetPasswd;
static NPIdentifier s_idGetMacAddress;
static NPIdentifier s_idGetHardwareInfo;
static NPIdentifier s_idSetEditSize;
static NPIdentifier s_idGetVersion;
//int result_size;
//NPString str1;
//NPUTF8 utf8_chars1[INVOKE_STRING_LENGTH];
CScriptObject(NPP npp);
~CScriptObject();
virtual bool hasProperty(NPIdentifier name);
virtual bool hasMethod(NPIdentifier name);
virtual bool getProperty(NPIdentifier name, NPVariant *result);
virtual bool setProperty(NPIdentifier name, const NPVariant *value);
virtual bool invoke(NPIdentifier name, const NPVariant *args, uint32_t argCount, NPVariant *result);
virtual bool invokeDefault(const NPVariant *args, uint32_t argCount, NPVariant *result);
virtual bool enumerate(NPIdentifier **identifier, uint32_t *count);
virtual bool construct(const NPVariant *args, uint32_t argCount,NPVariant *result);
};

CScriptObject对象继承ScriptablePluginObjectBase类,随后就是实现CScriptObject中的虚函数,以下为部分实现:

CScriptObject::CScriptObject(NPP npp):ScriptablePluginObjectBase(npp)
{
    s_idGetPasswd = NPN_GetStringIdentifier("GetPasswd");
    s_idGetMacAddress = NPN_GetStringIdentifier("GetMacAddress");
    s_idGetHardwareInfo = NPN_GetStringIdentifier("GetHardwareInfo");
    s_idSetEditSize = NPN_GetStringIdentifier("SetEditSize");
    s_idGetVersion = NPN_GetStringIdentifier("GetVersion");
    utf8_chars1 = static_cast<NPUTF8 *>(NPN_MemAlloc(INVOKE_STRING_LENGTH));
    utf8_chars2 = static_cast<NPUTF8 *>(NPN_MemAlloc(INVOKE_STRING_LENGTH));
    utf8_chars3 = static_cast<NPUTF8 *>(NPN_MemAlloc(INVOKE_STRING_LENGTH));
    utf8_chars4 = static_cast<NPUTF8 *>(NPN_MemAlloc(INVOKE_STRING_LENGTH));
}
CScriptObject::~CScriptObject()
{
    //NPN_MemAlloc分配的内存不需要手动释放,浏览器会自动释放
    //NPN_MemFree(utf8_chars1);
    //NPN_MemFree(utf8_chars2);
    //NPN_MemFree(utf8_chars3);
    //MessageBox(NULL,"CScriptobject delete","Plugin GetMacAddress",MB_ICONERROR|MB_OK);
}
bool CScriptObject::hasMethod(NPIdentifier name)
{
    //MessageBox(NULL,"HashMethod Successful","HashMethod",MB_ICONERROR|MB_OK);
    return  name == s_idGetPasswd || name == s_idGetMacAddress || name == s_idGetHardwareInfo || name == s_idSetEditSize || name == s_idGetVersion;
}
bool CScriptObject::hasProperty(NPIdentifier name)
{
    //return name == s_idSetEditSize;
    return false;
}
bool CScriptObject::getProperty(NPIdentifier name, NPVariant *result)
{
    return false;
}
bool CScriptObject::setProperty(NPIdentifier name, const NPVariant *value)
{
    return false;
}
bool CScriptObject::invoke(NPIdentifier name, const NPVariant *args, uint32_t argCount, NPVariant *result)
{
    char *tmp;
    CPlugin* plugin = (CPlugin*) mNpp->pdata;
    if (name == s_idGetPasswd)
    {
        tmp = plugin->myedit->GetVal();
        //*result = NPVARIANT_TO_STRING(tmp);
        memset(utf8_chars1,'\0',INVOKE_STRING_LENGTH);
        strcpy(utf8_chars1,tmp);
        //STRINGZ_TO_NPVARIANT(utf8_chars1,*result);
        //INT32_TO_NPVARIANT(10,*result);
        result->type = NPVariantType_String;
        NPString  str1 = {utf8_chars1, uint32_t(strlen(utf8_chars1)) };
        //str1.UTF8Characters = utf8_chars1;
        //str1.UTF8Length = uint32_t(strlen(utf8_chars1));
        result->value.stringValue = str1;
        return true;
    }
    if(name == s_idGetMacAddress){
        tmp = plugin->myedit->GetMacAddress();
        memset(utf8_chars2,'\0',INVOKE_STRING_LENGTH);
        strcpy(utf8_chars2,tmp);
        //STRINGZ_TO_NPVARIANT(utf8_chars2,*result);
        result->type = NPVariantType_String;
        NPString str2 = { utf8_chars2, uint32_t(strlen(utf8_chars2)) };
        result->value.stringValue = str2;
        return true;
    }
    if(name == s_idGetHardwareInfo){
        tmp = plugin->myedit->GetHardwareInfo();
        memset(utf8_chars3,'\0',INVOKE_STRING_LENGTH);
        strcpy(utf8_chars3,tmp);
        //STRINGZ_TO_NPVARIANT(utf8_chars3,*result);
        result->type = NPVariantType_String;
        NPString str3 = { utf8_chars3, uint32_t(strlen(utf8_chars3)) };
        result->value.stringValue = str3;
        return true;
    }
    if(name == s_idSetEditSize){
        //plugin->myedit->SetEditSize();
        if(args[0].type == NPVariantType_Int32 && args[1].type == NPVariantType_Int32){
            plugin->myedit->SetEditSize(NPVARIANT_TO_INT32(args[0]),NPVARIANT_TO_INT32(args[1]));
        }else if (args[0].type == NPVariantType_Double && args[1].type == NPVariantType_Double){
            plugin->myedit->SetEditSize((int)NPVARIANT_TO_DOUBLE(args[0]),(int)NPVARIANT_TO_DOUBLE(args[1]));
        }
        //INT32_TO_NPVARIANT(result_size,*result);
        return true;
}
    if(name == s_idGetVersion){
        tmp = plugin->myedit->GetVersion();
        memset(utf8_chars4,'\0',INVOKE_STRING_LENGTH);
        strcpy(utf8_chars4,tmp);
        result->type = NPVariantType_String;
        NPString str4 = { utf8_chars4, uint32_t(strlen(utf8_chars4)) };
        result->value.stringValue = str4;
        return true;
    }
    return false;
}

其中最主要的是hasMethod和invoke两个函数。当浏览器中JS调用某个插件函数A时,浏览器会通过NPObject调用hasMethod函数,以判断插件是否提供函数A的调用,若插件存在函数A(即hasMethod返回true),随后浏览器将调用invoke函数。故,在hasMethod函数中,我们将插件对外提供的函数在此处列出。

return  name == s_idGetPasswd || name == s_idGetMacAddress || name == s_idGetHardwareInfo || name == s_idSetEditSize || name == s_idGetVersion;

其中s_idGetPasswd等变量在CScriptObject进行了相应的初始化,以关联具体函数名。

CScriptObject::CScriptObject(NPP npp):ScriptablePluginObjectBase(npp)
{
    s_idGetPasswd = NPN_GetStringIdentifier("GetPasswd");
    s_idGetMacAddress = NPN_GetStringIdentifier("GetMacAddress");
    s_idGetHardwareInfo = NPN_GetStringIdentifier("GetHardwareInfo");
    s_idSetEditSize = NPN_GetStringIdentifier("SetEditSize");
    s_idGetVersion = NPN_GetStringIdentifier("GetVersion");
    ........
}

当通过hasMethod返回true之后,浏览器讲会调用invoke函数,在invoke函数中,通过判断name参数,确定浏览器需要具体调用插件中的哪个函数,随后调用该函数即可,具体可参照invoke实现代码。插件提供的各种函数在CYKedit中详细定义。

你可能感兴趣的:(基于NPAPI接口的windows平台安全控件开发(二))