NPAPI插件开发

plugin创建时,浏览器会创建一个子窗口,作为plugin的窗口(对于有窗口的plugin)。但是一直不知道这个子窗口怎么获取。仔细看上面的例子才发现,plugin类实现的不光是Plugin.cpp中的那几个函数,最重要的一个函数实现在Plugin.h中:NPBool init(NPWindow* pNPWindow) { m_bInitialized = TRUE; return TRUE;}
这个函数没有实现什么功能,但是注意这个函数的参数NPWindow*。啊哈!这个不就是我苦苦寻求的plugin窗口么?!!!
为了确认这一点,我就在这里下一个断点来看看,我觉得这个函数放在头文件中简直就是个错误,于是我将头文件中实现的三个函数都放到了.cpp文件中去实现了。

修改之后的头文件为:

#pragma once  
#include "pluginbase.h"  
class Plugin :  
public nsPluginInstanceBase  
{  
private:  
NPP m_pNPInstance;  
NPBool m_bInitialized;  
public:  
Plugin(NPP pNPInstance);  
~Plugin();  
  
NPBool init(NPWindow* pNPWindow);  
void shut();  
NPBool isInitialized();  
};  

移动到Plugin.cpp文件中实现的三个函数为:

NPBool Plugin::init(NPWindow* pNPWindow)  
{  
m_bInitialized = TRUE;  
return TRUE;  
}  
  
void Plugin::shut()  
{  
m_bInitialized = FALSE;  
}  
  
NPBool Plugin::isInitialized()  
{  
return m_bInitialized;  
}  

使用窗口句柄

从上面的调试过程可以发现,pNPWindow的window成员应该就是插件窗口的句柄了,根据相关资料进一步得知,对于windowed的plugin确实是窗口的句柄,而对于winless的plugin,window成员是一个HDC。因此,如果是winless的plugin,可以直接将pNPWindow->window强制转换为HDC然后在窗口中进行绘制、输出内容。
插件分为winless和windowed的,其主要区别是winless的插件窗口不是真正意义上的窗口只是网页的一部分,而windowed的插件窗口是浏览器的一个子窗口。由于winless的插件没有窗口所以其事件处理有一个专门的函数NPP_HandleEvent来对窗口的事件进行处理。对于winless的插件,事件响应函数就需要继承nsPluginInstanceBase类并实现函数HandleEvent;而对于windowed 的插件,插件窗口完全由插件控制,因此需要根据这个窗口句柄自行在插件中实现各种功能,包括事件消息响应等等。下面分别探讨在winless的插件和windowed的插件中如何使用窗口句柄。

winless插件

要创建winless的插件需要在新实例创建的时候指定为winless的,否则插件会默认创建为windowed的。修改NS_NewPluginInstance函数(该函数是我们自己实现的,在Plugin.cpp中,尽量不要修改np_entry.cpp、npn_gate.cpp、npp_gate.cpp这几个文件更不用说npapi.h了)。

nsPluginInstanceBase * NS_NewPluginInstance(nsPluginCreateData * aCreateDataStruct)  
{  
if(!aCreateDataStruct)  
return NULL;  
  
Plugin * plugin = new Plugin(aCreateDataStruct->instance);  
  
BOOL bWindowed = FALSE;  
NPN_SetValue(aCreateDataStruct->instance, NPPVpluginWindowBool, (void *)bWindowed);//winless插件需要在这里指出,默认为windowed  
  
return plugin;  
}  

用NPN_SetValue指定我们要创建的插件是winless的。上面的写法是中规中矩的写法,如果图省事可以写成NPN_SetValue(aCreateDataStruct->instance, NPPVpluginWindowBool, NULL);
本例参考sdk中sample目录下的winless例子来写的,有必要的话你可以参考那个例子。下面要实现一个功能:将插件窗口的背景设置为蓝色,另外按下鼠标左键会发出声音,鼠标移动时在插件窗口画线。
首先,为plugin类添加一个私有成员mWindow保存窗口指针,覆写基类的SetWindow函数,

NPError Plugin::SetWindow(NPWindow* Window)  
{  
mWindow=Window;  
return NPERR_NO_ERROR;  
}  

其他功能都要在事件处理函数中实现,因此基类的虚函数HandleEvent需要加以实现。

uint16_t Plugin::HandleEvent(void* aEvent)  
{  
NPEvent *event = (NPEvent *)aEvent;  
switch(event->event)  
{  
case WM_PAINT:  
{  
if(!mWindow)  
break;  
  
// get the dirty rectangle to update or repaint the whole window  
RECT * drc = (RECT *)event->lParam;  
if(drc)  
FillRect((HDC)event->wParam, drc, (HBRUSH)(COLOR_WINDOW));  
else {  
RECT rc;  
rc.bottom = mWindow->y + mWindow->height;  
rc.left = mWindow->x;  
rc.right = mWindow->x + mWindow->width;  
rc.top = mWindow->y;  
FillRect((HDC)event->wParam, &rc, (HBRUSH)(COLOR_WINDOW));  
  
}  
  
break;  
  
}  

下一个功能:按下鼠标左键时发声:

case WM_LBUTTONDOWN:  
Beep(1000,200);  
break;  

最后实现画线的功能,这也是真正展示处理窗口的window成员的地方:
首先添加私有成员变量: int m_oldX,m_OldY,m_newX,m_newY;
在左键按下的事件中记录一个坐标,将左键按下的代码修改为:

case WM_LBUTTONDOWN:  
Beep(1000,200);  
m_oldX=LOWORD(event->lParam);  
m_OldY=HIWORD(event->lParam);  
break;  

接着实现鼠标移动的事件响应代码:

case WM_MOUSEMOVE:  
{  
m_newX=LOWORD(event->lParam);  
m_newY=HIWORD(event->lParam);  
HDC hDC = (HDC)mWindow->window;  
MoveToEx(hDC,m_oldX,m_OldY,NULL);  
LineTo(hDC,m_newX,m_newY);  
m_oldX=m_newX;m_OldY=m_newY;  
break;  
}  

这里直接将mWindow->window强制转换为HDC,是因为对于winless的插件window成员在windows平台下就是该插件的HDC。注意由于这里有变量hDC的定义,因此这个case的代码必须用大括号括起来,否则会报错。下面是事件处理函数最后一点代码:

default:  
return 0;  
}  
return 1;  
}  

上面实现的有一个问题,就是只有窗口重绘的时候才会显示出画的线。

下面贴出前面这个项目的plugin.h文件及plugin.cpp文件的完整内容

#include "pluginbase.h"  
class Plugin :  
    public nsPluginInstanceBase  
{  
private:  
    NPP m_pNPInstance;  
    NPBool m_bInitialized;  
    NPWindow* mWindow;  
    int m_oldX,m_OldY,m_newX,m_newY;  
    bool bMdown;  
public:  
    Plugin(NPP pNPInstance);  
    ~Plugin();  
  
    NPBool init(NPWindow* pNPWindow);  
    void shut();  
    NPBool isInitialized();  
    NPError SetWindow(NPWindow* Window);  
    uint16_t HandleEvent(void* event);//windowed 插件窗口自己接收消息,不需handleevent。  
};  

//Plugin.cpp文件  
#include "Plugin.h"  
// functions /  
NPError NS_PluginInitialize()  
{  
    return NPERR_NO_ERROR;  
}  
void NS_PluginShutdown()  
{  
}  
nsPluginInstanceBase * NS_NewPluginInstance(nsPluginCreateData * aCreateDataStruct)  
{  
    if(!aCreateDataStruct)  
        return NULL;  
  
    Plugin * plugin = new Plugin(aCreateDataStruct->instance);  
  
    BOOL bWindowed = FALSE;  
    NPN_SetValue(aCreateDataStruct->instance, NPPVpluginWindowBool, (void *)bWindowed);//winless插件需要在这里指出,默认为windowed  
    return plugin;  
}  
void NS_DestroyPluginInstance(nsPluginInstanceBase * aPlugin)  
{  
    if(aPlugin)  
        delete (Plugin *)aPlugin;  
}  
  
// Plugin /  
Plugin::Plugin(NPP pNPInstance):nsPluginInstanceBase(),  
    m_pNPInstance(pNPInstance),  
    m_bInitialized(FALSE)  
{  
    bMdown=false;  
}  
  
NPBool Plugin::init(NPWindow* pNPWindow)  
{  
    m_bInitialized = TRUE;  
    return TRUE;  
}  
  
void Plugin::shut()  
{  
    m_bInitialized = FALSE;   
}  
  
NPBool Plugin::isInitialized()  
{  
    return m_bInitialized;  
}  
  
Plugin::~Plugin(void)  
{  
}  
  
NPError Plugin::SetWindow(NPWindow* Window)  
{  
    mWindow=Window;  
    return NPERR_NO_ERROR;  
}  
  
uint16_t Plugin::HandleEvent(void* aEvent)  
{  
    NPEvent *event = (NPEvent *)aEvent;  
    switch(event->event)  
    {  
    case WM_PAINT:    
        {  
            if(!mWindow)  
                break;  
  
// get the dirty rectangle to update or repaint the whole window  
            RECT * drc = (RECT *)event->lParam;  
            if(drc)  
                FillRect((HDC)event->wParam, drc, (HBRUSH)(COLOR_WINDOW));  
            else {  
                RECT rc;  
                rc.bottom = mWindow->y + mWindow->height;  
                rc.left   = mWindow->x;  
                rc.right  = mWindow->x + mWindow->width;  
                rc.top    = mWindow->y;  
                FillRect((HDC)event->wParam, &rc, (HBRUSH)(COLOR_WINDOW));  
  
            }  
  
            break;  
  
        }  
  
    case WM_LBUTTONDOWN:  
        Beep(1000,200);  
        m_oldX=LOWORD(event->lParam);  
        m_OldY=HIWORD(event->lParam);  
        bMdown=true;  
        break;  
  
    case WM_MOUSEMOVE:  
        {  
            m_newX=LOWORD(event->lParam);  
            m_newY=HIWORD(event->lParam);  
            if (bMdown)  
            {  
                HDC hDC = (HDC)mWindow->window;  
                MoveToEx(hDC,m_oldX,m_OldY,NULL);  
                LineTo(hDC,m_newX,m_newY);  
            }  
            NPN_ForceRedraw(m_pNPInstance);           
            m_oldX=m_newX;m_OldY=m_newY;  
            break;  
        }  
    case WM_LBUTTONUP:  
        bMdown=false;NPN_ForceRedraw(m_pNPInstance);break;  
    default:  
        return 0;  
    }  
//    
    return 1;  
}  

windowed插件

创建windowed的插件,可以这样:

BOOL bWindowed = TRUE;  
NPN_SetValue(aCreateDataStruct->instance, NPPVpluginWindowBool, (void *)bWindowed);//winless插件需要在这里指出,默认为windowed  

也可以什么都不写,因为默认就是创建windowed的插件。这个例子实现两个功能:设置背景并在创建窗口中输出一段文字。
首先,为Plugin类添加一个私有成员:HWND m_hWnd;
要对窗口进行处理需要使用到窗口子类化的函数,要包含头文件#include <windowsx.h>(Windows.h在其他头文件中已经包含了,这里可以不需要再写)
在plugin.cpp中声明一个回调函数和一个静态变量:

static LRESULT CALLBACK PluginWinProc(HWND, UINT, WPARAM, LPARAM);  
static WNDPROC lpOldProc = NULL;  

在类的构造函数中将m_hWnd初始化为NULL
修改Plugin::init为:

NPBool Plugin::init(NPWindow* pNPWindow)  
{  
mWindow=pNPWindow;  
m_hWnd =(HWND)pNPWindow->window;  
if (m_hWnd==NULL) return FALSE;  
// 对窗口进行子类化,这样就可以对消息进行处理并在窗口中进行绘制  
lpOldProc = SubclassWindow(m_hWnd, (WNDPROC)PluginWinProc);  
  
// 将窗口与 Plugin 对象相关联,这样就可以在窗口处理中访问Plugin 对象  
SetWindowLongPtr(m_hWnd, GWLP_USERDATA, (LONG_PTR)this);  
  
m_bInitialized = TRUE;  
return TRUE;  
}  

对于windowed的插件pNPWindow->window存储就是插件窗口的句柄,上面将其转换为HWND,并将窗口进行子类化,关键代码的作用请参看注释。将Plugin::shut修改为:

void Plugin::shut()  
{  
// 反子类化subclass it back  
SubclassWindow(m_hWnd, lpOldProc);  
m_hWnd = NULL;  
m_bInitialized = FALSE;  
}  

到这一步已经完成了对窗口句柄的获取与释放,最后还需要的就是消息处理函数了,如下:

static LRESULT CALLBACK PluginWinProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)  
{  
switch (msg) {  
case WM_PAINT:  
{  
// draw a frame and display some string  
PAINTSTRUCT ps;  
HDC hdc = BeginPaint(hWnd, &ps);  
RECT rc;  
GetClientRect(hWnd, &rc);  
FillRect( hdc, &rc, (HBRUSH) (COLOR_WINDOW));  
FrameRect(hdc, &rc, GetStockBrush(BLACK_BRUSH));  
Plugin * p = (Plugin*) GetWindowLongPtr(hWnd, GWLP_USERDATA);  
if(p) {  
char *s = "Hello , MY FIRST PLUGIN!";//p->GetGuiText();  
DrawText(hdc, s, strlen(s), &rc, DT_SINGLELINE | DT_CENTER | DT_VCENTER);  
}  
  
EndPaint(hWnd, &ps);  
}  
break;  
default:  
break;  
}  
  
return DefWindowProc(hWnd, msg, wParam, lParam);  
}  

这里只是对WM_PAINT消息进行了处理,其他消息用DefWindowProc进行默认的处理。在WM_PAINT的消息处理代码中我们为窗口设置了背景,并输出一段文字"Hello , MY FIRST PLUGIN!",没错,这就是我们的插件的hello world!!

最后这个项目的plugin.h文件和plugin.cpp文件完整内容

#include "pluginbase.h"  
class Plugin :  
    public nsPluginInstanceBase  
{  
private:  
    NPP m_pNPInstance;  
    NPBool m_bInitialized;  
    NPWindow* mWindow;  
    HWND m_hWnd;  
public:  
    Plugin(NPP pNPInstance);  
    ~Plugin();  
    NPBool init(NPWindow* pNPWindow);//  {  m_bInitialized = TRUE;  return TRUE;}  
    void shut();//  {  m_bInitialized = FALSE;  }  
    NPBool isInitialized();// {  return m_bInitialized;  }  
};  

//Plugin.cpp  
#include "Plugin.h"  
  
#include <windowsx.h>  
  
// functions /  
NPError NS_PluginInitialize()  
{  
    return NPERR_NO_ERROR;  
}  
  
void NS_PluginShutdown()  
{  
}  
  
nsPluginInstanceBase * NS_NewPluginInstance(nsPluginCreateData * aCreateDataStruct)  
{  
    if(!aCreateDataStruct)  
        return NULL;  
  
    Plugin * plugin = new Plugin(aCreateDataStruct->instance);  
  
    return plugin;  
}  
  
void NS_DestroyPluginInstance(nsPluginInstanceBase * aPlugin)  
{  
    if(aPlugin)  
        delete (Plugin *)aPlugin;  
}  
  
// Plugin /  
Plugin::Plugin(NPP pNPInstance):nsPluginInstanceBase(),  
    m_pNPInstance(pNPInstance),  
    m_bInitialized(FALSE)  
{  
    m_hWnd = NULL;  
}  
  
Plugin::~Plugin(void)  
{  
}  
  
static LRESULT CALLBACK PluginWinProc(HWND, UINT, WPARAM, LPARAM);  
static WNDPROC lpOldProc = NULL;  
  
NPBool Plugin::init(NPWindow* pNPWindow)  
{  
    mWindow=pNPWindow;  
    m_hWnd =(HWND)pNPWindow->window;  
    if (m_hWnd==NULL) return FALSE;  
    // 对窗口进行子类化,这样就可以对消息进行处理并在窗口中进行绘制  
    lpOldProc = SubclassWindow(m_hWnd, (WNDPROC)PluginWinProc);  
  
    // 将窗口与 Plugin 对象相关联,这样就可以在窗口处理中访问Plugin 对象  
    SetWindowLongPtr(m_hWnd, GWLP_USERDATA, (LONG_PTR)this);  
  
    m_bInitialized = TRUE;  
    return TRUE;  
}  
  
void Plugin::shut()  
{  
    // 将窗口子类化回去subclass it back  
    SubclassWindow(m_hWnd, lpOldProc);  
    m_hWnd = NULL;  
    m_bInitialized = FALSE;   
}  
  
NPBool Plugin::isInitialized()  
{  
    return m_bInitialized;  
}  
  
  
static LRESULT CALLBACK PluginWinProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)  
{  
  switch (msg) {  
    case WM_PAINT:  
      {  
        // draw a frame and display some string  
        PAINTSTRUCT ps;  
        HDC hdc = BeginPaint(hWnd, &ps);  
        RECT rc;  
        GetClientRect(hWnd, &rc);  
                FillRect( hdc, &rc, (HBRUSH) (COLOR_WINDOW));  
        FrameRect(hdc, &rc, GetStockBrush(BLACK_BRUSH));  
        Plugin * p = (Plugin*) GetWindowLongPtr(hWnd, GWLP_USERDATA);  
        if(p) {  
            char *s = "Hello , MY FIRST PLUGIN!";//p->GetGuiText();  
            DrawText(hdc, s, strlen(s), &rc, DT_SINGLELINE | DT_CENTER | DT_VCENTER);  
        }  
  
        EndPaint(hWnd, &ps);  
      }  
      break;  
    default:  
      break;  
  }  
  
  return DefWindowProc(hWnd, msg, wParam, lParam);  
}  


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值