Ironbin


  • 首页

  • 关于

  • 标签

  • 分类

  • 归档

  • 搜索

Android 9-Patch

发表于 2014-08-09 | 分类于 android | | 阅读次数:

9-Patch是Android设备上处理可扩展图片的方式。众所周知,Android设备的屏幕尺寸和分辨率众多,为了图片能完美的适配各种屏幕,Android采用了支持可扩展的图片的方案。

9-Patch的使用非常简便。围绕在图片周围的黑线便是定义之处。关于此格式需要注意的地方:

  • png图片的一周有 1px的边框,用于定义可扩展区域和静态区域(黑线)
  • 图片一周,黑线标识可扩展区域,黑线必须是 纯黑 (#000),不透明的,添加深灰色的线都不能起作用,其它区域必须是完全透明或者纯白色。
  • 可以使用任意图片处理工具处理,比如Windows的画板(MS Paint)也没有问题
  • 保存时使用扩展名 .9.png

下面说明黑线的作用。

Top和Left

Top和Left定义了将要被拉伸的部分。要注意的点:

  • 9-Patch图 只能拉伸,不是整块复制,不能收缩

  • 同一边可以定义多个区域,使用时将按照区域的宽度等比例拉伸

Right和Bottom

Right和Bottom定义了内容的区域。

常见的中文编码

发表于 2014-07-26 | 分类于 programming | | 阅读次数:

工作经常碰到编码、字符集等概念,下面收集了一下常见的中文编码,方便日后查阅。

常见的有国家标准的GB 2312-80,GB 18030-2005,以及并非国家标准但应用很广的GBK。

先说说这些名称的所表示的意思:标准类别代号 顺序号-年代号 中文名称。如”GB 2312-(19)80”, “GB 18030-2005”。

GB 2312

标准共收录6763个汉字,其中一级汉字3755个,二级汉字3008个;同时收录了包括拉丁字母、希腊字母、日文平假名及片假名字母、俄语西里尔字母在内的682个字符。

在使用GB2312的程序通常采用EUC储存方法,以便兼容于ASCII。浏览器编码表上的“GB2312”,通常都是指“EUC-CN”表示法。

每个汉字及符号以两个字节来表示。第一个字节称为“高位字节”,第二个字节称为“低位字节”。

“高位字节”使用了0xA1-0xF7(把01-87区的区号加上0xA0),“低位字节”使用了0xA1-0xFE(把01-94加上0xA0)。 由于一级汉字从16区起始,汉字区的“高位字节”的范围是0xB0-0xF7,“低位字节”的范围是0xA1-0xFE,占用的码位是72*94=6768。其中有5个空位是D7FA-D7FE。

例如“啊”字在大多数程序中,会以两个字节,0xB0(第一个字节)0xA1(第二个字节)储存。(与区位码对比:0xB0=0xA0+16,0xA1=0xA0+1)。

GBK

由于GB 2312-80只收录6763个汉字,有不少汉字,如部分在GB 2312-80推出以后才简化的汉字(如“啰”),部分人名用字(如中国前总理朱镕基的“镕”字),台湾及香港使用的繁体字,日语及朝鲜语汉字等,并未有收录在内。于是厂商微软利用GB 2312-80未使用的编码空间,收录GB 13000.1-93全部字符制定了GBK编码。

根据微软资料,GBK是对GB2312-80的扩展,也就是CP936字码表 (Code Page 936)的扩展(之前CP936和GB 2312-80一模一样),最早实现于Windows 95简体中文版。虽然GBK收录GB 13000.1-93的全部字符,但编码方式并不相同。

GBK自身并非国家标准,只是曾由国家技术监督局标准化司、电子工业部科技与质量监督司公布为“技术规范指导性文件”。原始GB13000一直未被业界采用,后续国家标准GB18030技术上兼容GBK而非GB13000。

字符有一字节和双字节编码,00–7F范围内是一位,和ASCII保持一致,此范围内严格上说有96个文字和32个控制符号。

之后的双字节中,前一字节是双字节的第一位。总体上说第一字节的范围是81–FE(也就是不含80和FF),第二字节的一部分领域在40–7E,其他领域在80–FE。

GB 18030

全称:国家标准GB 18030-2005《信息技术 中文编码字符集》,是中华人民共和国现时最新的内码字集,是GB 18030-2000《信息技术 信息交换用汉字编码字符集 基本集的扩充》的修订版。与GB 2312-1980完全兼容,与GBK基本兼容,支持GB 13000及Unicode的全部统一汉字,共收录汉字70244个。

GB 18030主要有以下特点:

代码页 54936
与 UTF-8 相同,采用多字节编码,每个字可以由1个、2个或4个字节组成。
编码空间庞大,最多可定义161万个字符。
支持中国国内少数民族的文字,不需要动用造字区。
汉字收录范围包含繁体汉字以及日韩汉字。

单字节,其值从0到0x7F。
双字节,第一个字节的值从0x81到0xFE,第二个字节的值从0x40到0xFE(不包括0x7F)。
四字节,第一个字节的值从0x81到0xFE,第二个字节的值从0x30到0x39,第三个字节从0x81到0xFE,第四个字节从0x30到0x39。

区别与联系

  • 容量:GB 2312 < GBK < GB 18030
  • GB 2312 和 GBK 是16位的,GB 18030 是32位
  • GB 18030, GBK 都是向后兼容,GB 18030兼容GBK,GBK兼容GB 2312

平常使用中,应该首选Unicode编码,保证在多语言环境下的兼容性。如果需要使用中文编码,首选GB 18030,能保证更多的汉字正常显示,如果终端不支持,可以按序向后换小一些的编码集。

32位和64位程序的注册表知识点

发表于 2014-07-06 | 分类于 windows | | 阅读次数:

众所周知,注册表是windows系统的数据库,系统本身以及安装的程序都依赖注册表。当windows进化到64位,还要兼容大量的32位老程序,便碰到了注册表的问题。这里只讨论64位系统的注册表机制。

注册表树最大可以有512级深度,通过注册表API一次可以创建32级深的键值。

为了解决64位系统遇到的问题,Windows使用了三套方案,共享(Shared)、注册表重定向(Registry Redirector).aspx)和注册表反射(Registry Reflection).aspx)。

  • 共享,顾名思义,保存可以被32位和64位共同使用的注册表。

  • 反射,这是一个早期的方案,只用于Windows Server 2008, Windows Vista, Windows Server 2003, 和Windows XP,从Windows 7 和 Windows Server 2008 R2 开始被移除。

    它的做法是备份和同步,就是把同一份注册表保存到两个物理位置,这两个位置会分别被32位或64位程序使用。保存发生在RegCloseKey调用结束。使用 RegDisableReflectionKey和RegEnableReflectionKey方法可以禁用/启用反射机制。

  • 重定向,主要使用的机制,它会区分32位和64位程序(Application),分别提供给他们不同的注册表物理存储位置,但会映射成同一个逻辑视图(View),这个过程对程序本身是透明的。也就是说,一个32位程序可以像在32位系统中一样来使用注册表,虽然它们在64位系统上被存储在不同的物理位置。

    32位程序重定向的注册表存放在Wow6432Node下,例如, HKEY_LOCAL_MACHINE\Software 会被重定向到 HKEY_LOCAL_MACHINE\Software\Wow6432Node。但是,这些操作应该由系统而非程序本身来做。

    当注册表包含%ProgramFiles% 或 %commonprogramfiles%时,64位系统会自动替换为 “%ProgramFiles(x86)%” 和 “%commonprogramfiles(x86)%”,同样的system32 被替换为 syswow64。具体的替换规则请阅读 MSDN.aspx)

关于Windows系统中哪些目录使用的是哪种机制,请更详细的列表.aspx)。

上面是系统的解决方案,如果你需要显式地访问64位注册表或32位注册表,可以使用KEY_WOW64_64KEY和KEY_WOW64_32KEY标识。

IOS要点总结

发表于 2014-06-16 | 分类于 ios | | 阅读次数:

总结ios开发遇到的易混知识点、陷阱

Frame VS Bounds

A picture worth thousand words! 见下图。

  • Frame: 视图的frame是相对父坐标系的原点和矩形,默认从左上点开始。

  • Bounds: 视图的bounds是相对自身坐标系的原点和区域。

  • Center: 中点是相对父坐标系的中心点。 注意middle是相对自身坐标系的中心点

注意,当view旋转(rotate)时:

1
frame.size != bounds.size

The Kitchen Drawer based on Stanford CS193p course

initWithFrame VS initWithCoder VS awakeFromNib

  • initWithFrame 是用户主动调用的,传入用来初始化的frame
  • initWithCoder 是被触发的。当UIView被反序列化(deserialize)后,被触发。
  • awakeFromNib 是被触发的。当UIView与xib内组件建立好连接(connection)后被触发。

注:initWithNibName:boundle是发送到UIViewController的消息,与上面的方法对象不同。

extension VS category

  • category是一种扩展已经存在的Class的方法。当你不能接触到Class的源码时,这是一种很有用的方式. 它常把文件取名为 “Class+CategoryName.h”, 比如 “NSView+CustomAdditions.h”, 包括.m的命名.

  • extension是一种特殊的category, 有以下两个不同:

    • 没有被扩展类名的category. 它的声明像这样:

      1
      2
      3
      @interface SomeClass ()
      - (void) anAdditionalMethod;
      @end
    • extension常定义在实现(.m)文件的顶部,然后在其下@implementation部分实现它。这也成为一种实现“伪私有”(pseudo-private)成员的方式。伪私有顾名思义,并不是真的声明了私有成员,只是达到了不能在外部访问的目的。

XCode 5 添加复制/删除行快捷键

发表于 2014-05-16 | 分类于 ios | | 阅读次数:

习惯了某些编辑器的复制一行和删除一行快捷方式后,使用xcode时没有相应快捷键有些不适应,下面是一种为其添加快捷键的方式。

修改快捷键配置文件的权限

1
2
3
sudo chmod 666 /Applications/Xcode.app/Contents/Frameworks/IDEKit.framework/Resources/IDETextKeyBindingSet.plist

sudo chmod 777 /Applications/Xcode.app/Contents/Frameworks/IDEKit.framework/Resources/

修改快捷键配置文件

  1. 添加Customized类别
  2. 添加Delete Current Line宏(macro)

    1
    selectLine:, cut:
  3. 添加Duplicate Current Line宏

    1
    selectLine:, copy:, moveToEndOfLine:, insertNewline:, paste:, deleteBackward:

 

绑定快捷键

重启 Xcode,打开菜单 Xcode > Preferences > KeyBindings。 找到上面定义的宏,为其定义快捷键。

Chromium 用户界面分析(1)

发表于 2014-05-05 | 分类于 chromium | | 阅读次数:

基础界面库的类结构

基础界面库位于src\ui\views目录下,定义了 跨平台 的组件,是Chromium界面的基础,顶层chrome/browser/ui/下的类都继承或使用这些基础类。

关于Widget静态图

下面是Widget及内部结构的类图,类关系做了简化,只显示较关键的联系。
chromium ui hierarchy

Widget内组件布局

  • 总体来说,Widget是最底层的UI组件(此处不区分Widget和NativeWidget,后面讨论其关系),与本地(Native)平台交互,处理平台相关的消息、事件等,转换到Chromium UI体系内。
  • 为了统一处理视图组件的消息事件,Widget内定义了一个根视图RootView
  • NonClientView是RootView的唯一孩子,也是其它所有视图的逻辑根 (为什么RootView和NonClientView不合并为一个视图?)
  • NonClientFrameView 是NonClientView孩子之一,负责非客户区的绘制和消息处理,根据不同的系统会有不同的实现,比如Windows下有OpaqueFrameView和GlassFrameView,非客户区一般包括窗口控制按钮(最大化,最小化,关闭)和标题栏(Title bar)
  • ClientView 是另一个孩子,负责客户区的绘制和消息处理,客户区包括工具栏、标签栏、地址栏、页面等。

 

//  The NonClientView is the logical root of all Views contained within a
//  Window, except for the RootView which is its parent and of which it is the
//  sole child. The NonClientView has two children, the NonClientFrameView which
//  is responsible for painting and responding to events from the non-client
//  portions of the window, and the ClientView, which is responsible for the
//  same for the client area of the window:
//
//  +- views::Widget ------------------------------------+
//  | +- views::RootView ------------------------------+ |
//  | | +- views::NonClientView ---------------------+ | |
//  | | | +- views::NonClientFrameView subclas  ---+ | | |
//  | | | |                                        | | | |
//  | | | | << all painting and event receiving >> | | | |
//  | | | | << of the non-client areas of a     >> | | | |
//  | | | | << views::Widget.                   >> | | | |
//  | | | |                                        | | | |
//  | | | +----------------------------------------+ | | |
//  | | | +- views::ClientView or subclass --------+ | | |
//  | | | |                                        | | | |
//  | | | | << all painting and event receiving >> | | | |
//  | | | | << of the client areas of a         >> | | | |
//  | | | | << views::Widget.                   >> | | | |
//  | | | |                                        | | | |
//  | | | +----------------------------------------+ | | |
//  | | +--------------------------------------------+ | |
//  | +------------------------------------------------+ |
//  +----------------------------------------------------+
//
// The NonClientFrameView and ClientView are siblings because due to theme
// changes the NonClientFrameView may be replaced with different
// implementations (e.g. during the switch from DWM/Aero-Glass to Vista Basic/
// Classic rendering).

类关系(Class Relationship)的建立

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
void Widget::Init(const InitParams& params) {
...
root_view_.reset(CreateRootView()); -->1
...
if (RequiresNonClientView(params.type)) {
non_client_view_ = new NonClientView; -->2
non_client_view_->SetFrameView(CreateNonClientFrameView()); -->3
// Create the ClientView, add it to the NonClientView and add the
// NonClientView to the RootView. This will cause everything to be parented.
non_client_view_->set_client_view(widget_delegate_->CreateClientView(this)); -->4
SetContentsView(non_client_view_); -->5
...
} else if (params.delegate) {
SetContentsView(params.delegate->GetContentsView());
...
}
...
}
```

1. Create root view
1. Create NonClientView and make Widget::non_client_view_ point to it
1. Create NonClientFrameView and add it to NonClientView
1. Create ClientFrameView and add it to NonClientView
1. Add NonClientView to RootView

### RootView类
要注意的几点:
- 从名字便可知,RootView是View体系(hierarchy)的根
- RootView起到中转的作用,把本地Widget相关的事件等转换到Chromium View体系中
- RootView只有一个孩子ContentView,大小正好布满整个区域

原文:

// RootView class
//
// The RootView is the root of a View hierarchy. A RootView is attached to a
// Widget. The Widget is responsible for receiving events from the host
// environment, converting them to views-compatible events and then forwarding
// them to the RootView for propagation into the View hierarchy.
//
// A RootView can have only one child, called its "Contents View" which is
// sized to fill the bounds of the RootView (and hence the client area of the
// Widget). Call SetContentsView() after the associated Widget has been
// initialized to attach the contents view to the RootView.
//

### Widget和NativeWidget的关系

Chromium UI的一个目标是实现 **跨平台** ,这样就抽象出一个与平台无关的Widget,而与平台相关特性则由NativeWidget处理。它们两个一一对应,可以设置为Widget拥有(owns)NativeWidget或NativeWidget拥有Widget。

////////////////////////////////////////////////////////////////////////////////
// Widget class
//
// Encapsulates the platform-specific rendering, event receiving and widget
// management aspects of the UI framework.
//
// Owns a RootView and thus a View hierarchy. Can contain child Widgets.
// Widget is a platform-independent type that communicates with a platform or
// context specific NativeWidget implementation.
//
// A special note on ownership:
//
// Depending on the value of the InitParams' ownership field, the Widget
// either owns or is owned by its NativeWidget:
//
// ownership = NATIVE_WIDGET_OWNS_WIDGET (default)
// The Widget instance is owned by its NativeWidget. When the NativeWidget
// is destroyed (in response to a native destruction message), it deletes
// the Widget from its destructor.
// ownership = WIDGET_OWNS_NATIVE_WIDGET (non-default)
// The Widget instance owns its NativeWidget. This state implies someone
// else wants to control the lifetime of this object. When they destroy
// the Widget it is responsible for destroying the NativeWidget (from its
// destructor).
//

&nbsp;
&nbsp;
&nbsp;

## Chromium界面的类结构

### 静态图
![chromium browser hierarchy](/images/posts/chromium_browser_hierarchy.jpg)

### 类关系(Class Relationship)的建立
1. Enter "Browser::Create" method
1. Call "InitBrowserWindow"
1. Call "CreateBrowserWindow" with the parameter "browser"(this)
1. Create "BrowserView" which is the subclass of "BrowserWindow"
1. Set up the relation of "BrowserView" and "BrowserFrame"
1. Return back the "Browser::window_" to "BrowserView" instance

Pseudo code:

```cpp
Browser::Create(){ -->1
...
browser->InitBrowserWindow(){ -->2
...
browser.window_ = BrowserWindow::CreateBrowserWindow(browser){ -->3,6
BrowserView* view = new BrowserView(browser); -->4
(new BrowserFrame(view))->InitBrowserFrame(); -->5
...
return view; -->6
}
...
}
...
}

 
 
 

总结

Chromium UI的目标是一套跨平台的方案,其中NativeWidget实现了平台相关的细节,Widget才是跨平台的组件,与NativeWidget做了映射。Widget是Windows组件,而Chromium UI的其它组件都是基于View建立的,所以需要一个RootView做中转,将窗口事件转换到View体系中,又作为其它View的根。

Chrome扩展+NPPlugin

发表于 2014-04-24 | 分类于 c/c++ | | 阅读次数:

扩展“Chrome扩展”

虽然基于强大的Javascript和Chrome扩展接口 (Extension API)所实现的扩展简单且强大,但是,如果你已经有一些实现的很不错的本地程序(Native Program)要通过浏览器复用怎么办?或者,浏览器的API不满足需求,你需要使用某些更强大的本地接口(Native API)时呢?

你可以配合Chrome扩展,实现NPAPI插件,通过Javascript与本地的二进制程序交互。

其实Chrome本身已经集成了一些NPAPI插件,比如PDF viewer,Flash player。

先简单区分一下扩展(Extension)和插件(Plguin),它们的共同点是都用于扩充增强浏览器的功能,

  • 扩展是基于 浏览器提供的接口 ,运行在浏览器的沙箱(Sandbox)中,不与本地代码交互,安全性可控,也意味着可扩展的功能有限。

  • 插件是基于与浏览器交互的框架(chrome用的是Mozilla的NPAPI框架),实现方式为本地二进制代码,插件其实就是能通过框架与浏览器通信的 本地程序 。它可以不运行在沙箱中,所以权限很大,实现不好很容易造成浏览器的漏洞。

 
 
 

实现NPAPI插件

创建动态库(DLL)项目

  1. 创建空项目
    本文通过VS2010创建,首选新建Win32|Win32 Project, 然后在创建向导中选择DLL, empty project。

  2. 复制示例代码
    将示例包(文章附件)以下文件复制到项目中:

    common/*
    include/*
    lib/*
    Plugin.cpp
    Plugin.h
    PluginObject.cpp
    PluginObject.h
    

配置项目(Project)属性

  1. 修改输出的动态库名
    修改Configuration Properties | General | Target Name,注意Mozilla规范要求,插件 必须以“np”开头 。

  2. 添加Include目录
    在Configuration Properties | C/C++ | General | Additional Include Directories添加自定义目录include。

  3. 添加第三方库
    这里要和前端JS交互,所以添加C++的第三方JSON库,
    在Configuration Properties | Linker | General | Additional Library Directories添加链接库目录lib。
    然后在Configuration Properties | Linker | Input | Additional Dependencies里添加库文件lib_json.lib。

  4. 添加宏定义(Preprocessor)
    在Configuration Properties | C/C++ | Preprocessor Definitions添加宏 WIN32;_WINDOWS;XP_WIN32;XP_WIN;X86;。

  5. 禁止预编译头(Precompiled header)
    Configuration Properties | C/C++ | Precompiled Header为“Not Using Precompiled Headers”。

  6. 添加.def文件
    在Configuration Properties | Linker | Input | Module Definition File中添加NPHello.def。

  7. 修改Runtime Library
    这一步比较关键,在发布插件时去除debug符号,以“ /MT ”方式编译,不然在某些系统会遇到缺少运行时库的问题。
    Configuration Properties | C/C++ | Code Generation | Runtime Library设置为/MT。

实现插件

  1. 新建.def文件
    在项目中新建NPHello.def:

    1
    2
    3
    4
    5
    LIBRARY	"nphello"		
    EXPORTS
    NP_GetEntryPoints @1
    NP_Initialize @2
    NP_Shutdown @3
  2. 修改mimetype
    npp_gate.cpp:

    1
    2
    3
    4
    char *NPP_GetMIMEDescription(void)
    {
    return "application/x-hello";
    }

    NPHello.rc:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    BEGIN
    VALUE "CompanyName", "XB"
    VALUE "FileDescription", "XB Hello Module"
    VALUE "FileVersion", "1, 0, 0, 1"
    VALUE "InternalName", "nphello.dll"
    VALUE "LegalCopyright", "Copyright (C) 2013"
    VALUE "MIMEType", "application/x-hello"
    VALUE "OriginalFilename", "nphello.dll"
    VALUE "ProductName", "NPHello"
    VALUE "ProductVersion", "1, 0, 0, 1"
    END
  3. 删除NPP_GetJavaClass
    np_entry.cpp

    1
    2
    3
    4
    NPNFuncs.getJavaEnv              = NULL;
    NPNFuncs.getJavaPeer = NULL;
    ...
    pluginFuncs->javaClass = NULL;
  4. 定义与Javascript交互的接口
    PluginObject.cpp

    1
    2
    3
    4
    // define function name called by JS
    const char* kPluginFunction = "function";
    // define property name set by JS
    const char* kPluginCallback = "callback";

    接口1:用于JS查询Plugin对象是否有该方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    bool PluginObject::hasMethod(NPIdentifier methodName)
    {
    bool bRev = false;
    NPUTF8 *pName = NPNFuncs.utf8fromidentifier(methodName);

    if (strcmp(pName, kPluginFunction) ==0){
    return true;
    } else {
    return false;
    }
    }
接口2:用于JS查询Plguin对象是否有该属性:

1
2
3
4
5
6
7
8
9
10
11
12
bool PluginObject::hasProperty(NPIdentifier propertyName)
{
bool bRev = false;
NPUTF8 *pName = NPNFuncs.utf8fromidentifier(propertyName);

if (pName!=NULL){
if (strcmp(pName, kPluginCallback) == 0){
return true;
}
}
return bRev;
}
  1. Javascript调用Plugin的入口

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    bool PluginObject::invoke(NPIdentifier methodName,
    const NPVariant* args, uint32_t argCount, NPVariant* result) {

    char* name = NPNFuncs.utf8fromidentifier(methodName);
    bool ret_val = false;
    std::string outString;

    if (!name) {
    return ret_val;
    }
    if (strcmp(name, kPluginFunction)==0){
    ret_val = true;
    hThread = CreateThread(NULL, 0, FunctionThread, this, 0, NULL);
    outString = "Called plugin method from external.";
    } else {
    // Exception handling.
    outString = "Called an invalid method.";
    }
    char* npOutString = (char *)NPNFuncs.memalloc(outString.length() + 1);
    if (!npOutString)
    return false;
    strcpy_s(npOutString, outString.length()+1, outString.c_str());
    STRINGZ_TO_NPVARIANT(npOutString, *result);

    NPNFuncs.memfree(name);
    return ret_val;
    }
  2. 在新的线程中执行插件的逻辑

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    // create a threed
    DWORD WINAPI FunctionThread(LPVOID lparam){
    PluginObject* pluginObj = (PluginObject*)lparam;
    if (pluginObj==NULL)
    return false;

    pluginObj->PluginFunction();
    return true;
    }

    void PluginObject::PluginFunction(){
    try{
    Sleep(3000);
    Json::Value root;
    root["msg"] = "Plugin process complete with three minutes!";
    CallJSFunction(root.toStyledString());
    } catch(HRESULT hr){
    _com_error error(hr);
    std::wstring msg(error.ErrorMessage());
    Json::Value root;
    root["msg"] = utf8_encode(msg);
    CallJSFunction(root.toStyledString());
    }
    }
  3. 当插件执行完毕,回调Javascript的方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    void PluginObject::CallJSFunction(std::string result){
    int iRev = 0;

    if (mJSFunction != NULL){
    // 转换参数列表
    NPVariant relements[1];
    STRINGZ_TO_NPVARIANT(result.c_str(), relements[0]);

    // 调用JS函数
    NPVariant jsResult;
    NPN_InvokeDefault(npp, mJSFunction, relements, 1, &jsResult);

    if (NPVARIANT_IS_STRING(jsResult)){
    NPString rString = NPVARIANT_TO_STRING(jsResult);

    char revBuf[255] = {0};
    memcpy(revBuf, rString.UTF8Characters, rString.UTF8Length);
    }

    // 释放从浏览器那获取的结果
    NPN_ReleaseVariantValue(&jsResult);
    }
    return;
    }

编译项目

以Release方式编译

 
 
 

实现一个简单的扩展

Manifest

注意plugins属性,如果插件只希望被本扩展调用,那将public设置成false。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
...

"browser_action": {

"default_popup": "popup.html",
},

"plugins": [
{ "path": "plugins/nphello.dll", "public":false }
],

"permissions": [
"*://*/*"
]
}

popup.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Hello</title>
</head>

<body>
<div></div>
</body>

<embed id="pluginObj" type="application/x-hello" style="width:0;height:0;"></embed>
<script src="js/jquery.min.js"></script>
<script src="js/popup.js"></script>
</html>

popup.js

先设置回调函数,再去调用插件的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function callback(result){
result = JSON.parse(result);
print('<p class="npp"> [ NPP ]: '+ result['msg']+'</p>')
clearInterval(timer);
}

document.addEventListener('DOMContentLoaded', function () {
pluginObj = document.getElementById("pluginObj");
if (typeof pluginObj == "object") {
pluginObj.callback = callback;
print('<p class="js"> [ JS ]: I\'m calling Plugin function!</p>')
var msg = pluginObj.function();
print('<p class="npp"> [ NPP ]: '+ msg+'</p>')
}
timer = setInterval(function(){count++; print('<p class="js"> [ JS ]: Hey, I\'ve been waiting '+500*count+' ms...')}, 500);
}, false);

运行结果

 
 
 

附:chrome extension+npplugin

扩展阅读:使用 VS 编译 NPAPI 插件

1…1112
程学彬

程学彬

117 日志
48 分类
99 标签
Creative Commons
Links
  • ShinySky
0%
© 2014 — 2020 程学彬