如何在Windows下开发NodeJS的CC++原生扩展

JavaScript022

如何在Windows下开发NodeJS的CC++原生扩展,第1张

一、编写Node.js原生扩展

Node.js是一个强大的平台,理想状态下一切都都可以用javascript写成。然而,你可能还会用到许多遗留的库和系统,这样的话使用c++编写Node.JS扩展会是一个不错的注意。

以下所有例子的源代码可在node扩展示例中找到 。

编写Node.js C + +扩展很大程度上就像是写V8的扩展; Node.js增加了一些接口,但大部分时间你都是在使原始的V8数据类型和方法,为了理解以下的代码,你必须首先阅读V8引擎嵌入指南。

Javascript版本的Hello World

在讲解C++版本的例子之前,先让我们来看看在Node.js中用Javascript编写的等价模块是什么样子。这是一个最简单的Hello World,也不是通过HTTP,但它展示了node模块的结构,而其接口也和大多数C++扩展要提供的接口差不多:

HelloWorldJs = function() {

this.m_count = 0

}

HelloWorldJs.prototype.hello = function()

{

this.m_count++

return “Hello World”

}

exports.HelloWorldJs = HelloWorldJs

正如你所看到的,它使用prototype为HelloWorldJs类创建了一个新的方法。请注意,上述代码通过将HelloWorldJS添加到exports变量来暴露构造函数。

要在其他地方使用该模块,请使用如下代码:

var helloworld = require(‘helloworld_js’)

var hi = new helloworld.HelloWorldJs()

console.log(hi.hello())// prints “Hello World” to stdout

C++版本的Hello World

要开始编写C++扩展,首先要能够编译Node.js(请注意,我们使用的是Node.js 2.0版本)。本文所讲内容应该兼容所有未来的0.2.x版本。一旦编译安装完node,编译模块就不在需要额外的东西了。

完整的源代码可以在这里找到 。在使用Node.js或V8之前,我们需要包括相关的头文件:

#include <v8.h>

#include <node.h>

using namespace node

using namespace v8

在本例子中我直接使用了V8和node的命名空间,使代码更易于阅读。虽然这种用法和谷歌的自己的C++编程风格指南相悖,但由于你需要不停的使用V8定义的类型,所以目前为止的大多数node的扩展仍然使用了V8的命名空间。

接下来,声明HelloWorld类。它继承自node::ObjectWrap类 ,这个类提供了几个如引用计数、在V8内部传递contex等的实用功能。一般来说,所有对象应该继承ObjectWrap:

class HelloWorld: ObjectWrap

{

private:

int m_count

public:

声明类之后,我们定义了一个静态成员函数,用来初始化对象并将其导入Node.js提供的target对象中。设个函数基本上是告诉Node.js和V8你的类是如何创建的,和它将包含什么方法:

static Persistent<FunctionTemplate>s_ct

static void Init(Handle<Object>target)

{

HandleScope scope

Local<FunctionTemplate>t = FunctionTemplate::New(New)

s_ct = Persistent<FunctionTemplate>::New(t)

s_ct->InstanceTemplate()->SetInternalFieldCount(1)

s_ct->SetClassName(String::NewSymbol(“HelloWorld”))

NODE_SET_PROTOTYPE_METHOD(s_ct, “hello”, Hello)

target->Set(String::NewSymbol(“HelloWorld”),

s_ct->GetFunction())

}

在上面这个函数中target参数将是模块对象,即你的扩展将要载入的地方。(译著:这个函数将你的对象及其方法连接到

这个模块对象,以便外界可以访问)首先我们为New方法创建一个FunctionTemplate,将于稍后解释。我们还为该对象添加一个内部字段,并命

名为HelloWorld。然后使用NODE_SET_PROTOTYPE_METHOD宏将hello方法绑定到该对象。最后,一旦我们建立好这个函数模板后,将他分配给target对象的HelloWorld属性,将类暴露给用户。

接下来的部分是一个标准的C++构造函数:

HelloWorld() :

m_count(0)

{

}

~HelloWorld()

{

}

接下来,在::New 方法中V8引擎将调用这个简单的C++构造函数:

static Handle<Value>New(const Arguments&args)

{

HandleScope scope

HelloWorld* hw = new HelloWorld()

hw->Wrap(args.This())

return args.This()

}

此段代码相当于上面Javascript代码中使用的构造函数。它调用new HelloWorld

创造了一个普通的C++对象,然后调用从ObjectWrap继承的Wrap方法,

它将一个C++HelloWorld类的引用保存到args.This()的值中。在包装完成后返回args.This(),整个函数的行为和

javascript中的new运算符类似,返回this指向的对象。

现在我们已经建立了对象,下面介绍在Init函数中被绑定到hello的函数:

static Handle<Value>Hello(const Arguments&args)

window.location 对象

window.location 对象用于获得当前页面的地址 (URL),并把浏览器重定向到新的页面。window.location 对象在编写时可不使用 window 这个前缀。

location.hostname 返回 web 主机的域名

location.pathname 返回当前页面的路径和文件名

location.port 返回 web 主机的端口 (80 或 443)

location.protocol 返回所使用的 web 协议(http:// 或 https://)

window.location.href= "http://www.xxxxxxxx.net" 跳转后有后退功能

window.location.replace("http://www.xxxxxxxx.net") 跳转后没有后退功能

window.open("http://www.xxxxxxxx.net") 在新的窗口打开链接,一般用于简单的弹出页面,现在基本上都被屏蔽掉 

window.location.reload( )刷新当前页面.

parent.location.reload( )刷新父亲对象(用于框架)

opener.location.reload( )刷新父窗口对象(用于单开窗口)

top.location.reload( )刷新最顶端对象(用于多开窗口)

window.history 对象

window.history 对象包含浏览器的历史。window.history对象在编写时可不使用 window 这个前缀。

window.history.back() - 加载历史列表中的前一个 URL,与在浏览器点击后退按钮相同,

window.history.forward() -加载历史列表中的下一个 URL。 与在浏览器中点击按钮向前相同

window.navigator 对象

window.navigator 对象包含有关访问者浏览器的信息,来自 navigator 对象的信息具有误导性,不应该被用于检测浏览器版本,这是因为:

navigator 数据可被浏览器使用者更改

一些浏览器对测试站点会识别错误

浏览器无法报告晚于浏览器发布的新操作系统

window.navigator 对象在编写时可不使用 window 这个前缀。

window.navigator.appCodeName返回浏览器的代码名。

window.navigator.appName返回代表浏览器名的字符串。

window.navigator.appMinorVersion返回浏览器的次版本号。该属性是一个只读的字符串。仅IE有效。

window.navigator.userAgent返回代表浏览器名和版本号的字符串。

window.navigator.platform返回浏览器平台的字符串("Win32", "Win16", "WinCE", "Mac68k", "MacPPC", "HP-UX", "SunOS" 等)。

window.navigator.cpuClass 返回CPU的信息("x86", "68K", "Alpha", "PPC" 等)。仅IE有效。

window.navigator.browserLanguage返回浏览器的语言种类。仅IE有效。

window.navigator.systemLanguage返回系统的语言种类。仅IE有效。

window.navigator.userLanguage userLanguage 返回用户环境的语言种类。仅IE有效。

window.navigator.cookieEnabled返回 cookie 是否可用的真伪值。

window.navigator.onLine返回是否能连上网络的真伪值。

window.navigator.javaEnabled()返回 Java 是否可用的真伪值。

window.navigator.userProfile保存着用户信息的对象。拥有 addReadRequest() doReadRequest() getAttribute() clearRequest() 等方法。

window.navigator.taintEnabled() 是否可以加密数据的真伪值。仅IE有效。

window对象的一些其它方法

setInterval() 和 setTimeout() 是 HTML DOM Window对象的两个方法。

window.setInterval() - 间隔指定的毫秒数不停地执行指定的代码。

window.setTimeout() - 暂停指定的毫秒数后执行指定的代码

window.clearInterval() 方法用于停止 setInterval() 方法执行的函数代码。

window.clearTimeout() 方法用于停止执行setTimeout()方法的函数代码。

window.alert()- 警告框经常用于确保用户可以得到某些信息。当警告框出现后,用户需要点击确定按钮才能继续进行操作。

window.prompt()- 确认框用于使用户可以验证或者接受某些信息。当确认框出现后,用户需要点击确定或者取消按钮才能继续进行操作。如果用户点击确认,那么返回值为 true。如果用户点击取消,那么返回值为 false。

window.confirm()- 提示框经常用于提示用户在进入页面前输入某个值。当提示框出现后,用户需要输入某个值,然后点击确认或取消按钮才能继续操纵。如果用户点击确认,那么返回值为输入的值。如果用户点击取消,那么返回值为 null。

window.open() - 打开新窗口

window.close() - 关闭当前窗口

window.moveTo() - 移动当前窗口

window.resizeTo() - 调整当前窗口的尺寸

window.frameElement 获取当前文档的宿主节点iframe元素

window.execScript() execScript函数与eval的功能相同,不同的是eval函数执行后的脚本的作用域是当前执行上下文,而execScript则总是针对全局作用域。