使用Node.js为其他程序编写扩展的基本方法

   这篇文章主要介绍了使用Node.js为其他程序编写扩展的基本方法 ,文中示例是通过Node让JavaScript代码与C++应用产生交互,需要的朋友可以参考下

  准备开始

  首先我们用下面的目录结构来创建一个节点通知(node-notify)文件夹.

   代码如下:

  .

  |-- build/ # This is where our extension is built.

  |-- demo/

  | `-- demo.js # This is a demo Node.js script to test our extension.

  |-- src/

  | `-- node_gtknotify.cpp # This is the where we do the mapping from C++ to Javascript.

  `-- wscript # This is our build configuration used by node-waf

  这个看起来很漂亮的tree 用通用的 tree 生成.

  现在让我来创建测试脚本demo.js 和决定我们扩展的API前期看起来应该像:

  ?

  1

  2

  3

  4

  5

  6

  7

  8// This loads our extension on the notify variable.

  // It will only load a constructor function, notify.notification().

  var notify = require("../build/default/gtknotify.node"); // path to our extension

  var notification = new notify.notification();

  notification.title = "Notification title";

  notification.icon = "emblem-default"; // see /usr/share/icons/gnome/16x16

  notification.send("Notification message");

  编写我们的Node.js扩展

  Init方法

  为了创建一个Node.js扩展,我们需要编写一个继承node::ObjectWrap的C++类。 ObjectWrap 实现了让我们更容易与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

  25

  26

  27

  28

  29

  30

  31

  32

  33

  34

  35

  36

  37#include // v8 is the Javascript engine used by QNode

  #include

  // We will need the following libraries for our GTK+ notification

  #include

  #include

  #include

  using namespace v8;

  class Gtknotify : node::ObjectWrap {

  private:

  public:

  Gtknotify() {}

  ~Gtknotify() {}

  static void Init(Handle target) {

  // This is what Node will call when we load the extension through require(), see boilerplate code below.

  }

  };

  /*

  * WARNING: Boilerplate code ahead.

  *

  * See https://www.cloudkick.com/blog/2010/aug/23/writing-nodejs-native-extensions/ & http://www.freebsd.org/cgi/man.cgi?query=dlsym

  *

  * Thats it for actual interfacing with v8, finally we need to let Node.js know how to dynamically load our code.

  * Because a Node.js extension can be loaded at runtime from a shared object, we need a symbol that the dlsym function can find,

  * so we do the following:

  */

  v8::Persistent Gtknotify::persistent_function_template;

  extern "C" { // Cause of name mangling in C++, we use extern C here

  static void init(Handle target) {

  Gtknotify::Init(target);

  }

  // @see http://github.com/ry/node/blob/v0.2.0/src/node.h#L101

  NODE_MODULE(gtknotify, init);

  }

  现在,我们必须把下面的代码编写到我们的Init()方法中:

  声明构造函数,并将其绑定到我们的目标变量。var n = require("notification");将绑定notification() 到 n:n.notification().

  ?

  1

  2

  3

  4

  5

  6

  7

  8

  9

  10

  11

  12

  13

  14// Wrap our C++ New() method so that it's accessible from Javascript

  // This will be called by the new operator in Javascript, for example: new notification();

  v8::Local local_function_template = v8::FunctionTemplate::New(New);

  // Make it persistent and assign it to persistent_function_template which is a static attribute of our class.

  Gtknotify::persistent_function_template = v8::Persistent::New(local_function_template);

  // Each JavaScript object keeps a reference to the C++ object for which it is a wrapper with an internal field.

  Gtknotify::persistent_function_template->InstanceTemplate()->SetInternalFieldCount(1); // 1 since a constructor function only references 1 object

  // Set a "class" name for objects created with our constructor

  Gtknotify::persistent_function_template->SetClassName(v8::String::NewSymbol("Notification"));

  // Set the "notification" property of our target variable and assign it to our constructor function

  target->Set(String::NewSymbol("notification"), Gtknotify::persistent_function_template->GetFunction());

  声明属性:n.title 和n.icon.

  ?

  1

  2

  3

  4

  5// Set property accessors

  // SetAccessor arguments: Javascript property name, C++ method that will act as the getter, C++ method that will act as the setter

  Gtknotify::persistent_function_template->InstanceTemplate()->SetAccessor(String::New("title"), GetTitle, SetTitle);

  Gtknotify::persistent_function_template->InstanceTemplate()->SetAccessor(String::New("icon"), GetIcon, SetIcon);

  // For instance, n.title = "foo" will now call SetTitle("foo"), n.title will now call GetTitle()

  声明原型方法:n.send()

  ?

  1

  2

  3// This is a Node macro to help bind C++ methods to Javascript methods (see https://github.com/joyent/node/blob/v0.2.0/src/node.h#L34)

  // Arguments: our constructor function, Javascript method name, C++ method name

  NODE_SET_PROTOTYPE_METHOD(Gtknotify::persistent_function_template, "send", Send);

  现在我们的Init()方法看起来应该是这样的:

  ?

  1

  2

  3

  4

  5

  6

  7

  8

  9

  10

  11

  12

  13

  14

  15

  16

  17

  18

  19

  20

  21

  22// Our constructor

  static v8::Persistent persistent_function_template;

  static void Init(Handle target) {

  v8::HandleScope scope; // used by v8 for garbage collection

  // Our constructor

  v8::Local local_function_template = v8::FunctionTemplate::New(New);

  Gtknotify::persistent_function_template = v8::Persistent::New(local_function_template);

  Gtknotify::persistent_function_template->InstanceTemplate()->SetInternalFieldCount(1); // 1 since this is a constructor function

  Gtknotify::persistent_function_template->SetClassName(v8::String::NewSymbol("Notification"));

  // Our getters and setters

  Gtknotify::persistent_function_template->InstanceTemplate()->SetAccessor(String::New("title"), GetTitle, SetTitle);

  Gtknotify::persistent_function_template->InstanceTemplate()->SetAccessor(String::New("icon"), GetIcon, SetIcon);

  // Our methods

  NODE_SET_PROTOTYPE_METHOD(Gtknotify::persistent_function_template, "send", Send);

  // Binding our constructor function to the target variable

  target->Set(String::NewSymbol("notification"), Gtknotify::persistent_function_template->GetFunction());

  }

  剩下要做的就是编写我们在Init方法中用的C++方法:New,GetTitle,SetTitle,GetIcon,SetIcon,Send

  构造器方法: New()

  New() 方法创建了我们自定义类的新实例(一个 Gtknotify 对象),并设置一些初始值,然后返回该对象的 JavaScript 处理。这是 JavaScript 使用 new 操作符调用构造函数的期望行为。

  ?

  1

  2

  3

  4

  5

  6

  7

  8

  9

  10

  11

  12

  13

  14

  15

  16

  17

  18std::string title;

  std::string icon;

  // new notification()

  static Handle New(const Arguments& args) {

  HandleScope scope;

  Gtknotify* gtknotify_instance = new Gtknotify();

  // Set some default values

  gtknotify_instance->title = "Node.js";

  gtknotify_instance->icon = "terminal";

  // Wrap our C++ object as a Javascript object

  gtknotify_instance->Wrap(args.This());

  return args.This();

  }

  getters 和 setters: GetTitle(), SetTitle(), GetIcon(), SetIcon()

  下面主要是一些样板代码,可以归结为 C++ 和 JavaScript (v8) 之间的值转换。

  ?

  1

  2

  3

  4

  5

  6

  7

  8

  9

  10

  11

  12

  13

  14

  15

  16

  17

  18

  19

  20

  21

  22

  23

  24// this.title

  static v8::Handle GetTitle(v8::Local property, const v8::AccessorInfo& info) {

  // Extract the C++ request object from the JavaScript wrapper.

  Gtknotify* gtknotify_instance = node::ObjectWrap::Unwrap(info.Holder());

  return v8::String::New(gtknotify_instance->title.c_str());

  }

  // this.title=

  static void SetTitle(Local property, Local value, const AccessorInfo& info) {

  Gtknotify* gtknotify_instance = node::ObjectWrap::Unwrap(info.Holder());

  v8::String::Utf8Value v8str(value);

  gtknotify_instance->title = *v8str;

  }

  // this.icon

  static v8::Handle GetIcon(v8::Local property, const v8::AccessorInfo& info) {

  // Extract the C++ request object from the JavaScript wrapper.

  Gtknotify* gtknotify_instance = node::ObjectWrap::Unwrap(info.Holder());

  return v8::String::New(gtknotify_instance->icon.c_str());

  }

  // this.icon=

  static void SetIcon(Local property, Local value, const AccessorInfo& info) {

  Gtknotify* gtknotify_instance = node::ObjectWrap::Unwrap(info.Holder());

  v8::String::Utf8Value v8str(value);

  gtknotify_instance->icon = *v8str;

  }

  原型方法: Send()

  首先我们抽取 C++ 对象的 this 引用,然后使用对象的属性来构建通知并显示。

  ?

  1

  2

  3

  4

  5

  6

  7

  8

  9

  10

  11

  12

  13

  14

  15

  16

  17

  18// this.send()

  static v8::Handle Send(const Arguments& args) {

  v8::HandleScope scope;

  // Extract C++ object reference from "this"

  Gtknotify* gtknotify_instance = node::ObjectWrap::Unwrap(args.This());

  // Convert first argument to V8 String

  v8::String::Utf8Value v8str(args[0]);

  // For more info on the Notify library: http://library.gnome.org/devel/libnotify/0.7/NotifyNotification.html

  Notify::init("Basic");

  // Arguments: title, content, icon

  Notify::Notification n(gtknotify_instance->title.c_str(), *v8str, gtknotify_instance->icon.c_str()); // *v8str points to the C string it wraps

  // Display the notification

  n.show();

  // Return value

  return v8::Boolean::New(true);

  }

  编译扩展

  node-waf 是一个构建工具,用来编译 Node 的扩展,这是 waf 的基本封装。构建过程可通过名为 wscript 的文件进行配置。

  ?

  1

  2

  3

  4

  5

  6

  7

  8

  9

  10

  11

  12

  13

  14

  15

  16

  17def set_options(opt):

  opt.tool_options("compiler_cxx")

  def configure(conf):

  conf.check_tool("compiler_cxx")

  conf.check_tool("node_addon")

  # This will tell the compiler to link our extension with the gtkmm and libnotifymm libraries.

  conf.check_cfg(package='gtkmm-2.4', args='--cflags --libs', uselib_store='LIBGTKMM')

  conf.check_cfg(package='libnotifymm-1.0', args='--cflags --libs', uselib_store='LIBNOTIFYMM')

  def build(bld):

  obj = bld.new_task_gen("cxx", "shlib", "node_addon")

  obj.cxxflags = ["-g", "-D_FILE_OFFSET_BITS=64", "-D_LARGEFILE_SOURCE", "-Wall"]

  # This is the name of our extension.

  obj.target = "gtknotify"

  obj.source = "src/node_gtknotify.cpp"

  obj.uselib = ['LIBGTKMM', 'LIBNOTIFYMM']

  现在我们已经准备好要开始构建了,在顶级目录下运行如下命令:

  node-waf configure && node-waf build

  如果一切正常,我们将得到编译过的扩展,位于:./build/default/gtknotify.node ,来试试:

  ?

  1

  2

  3

  4

  5

  6$ node

  > var notif = require('./build/default/gtknotify.node');

  > n = new notif.notification();

  { icon: 'terminal', title: 'Node.js' }

  > n.send("Hello World!");

  true

  上述的代码将在你的屏幕右上方显示一个通知信息。

  打成npm包

  这是非常酷的, 但是怎样与Node社区分享你的努力的成果呢? 这才是npm主要的用途: 使它更加容易扩展和分发.

  打npm的扩展包是非常简单的. 你所要做的就是在你的顶级目录中创建一个包含你的扩展信息的文件package.json :

  ?

  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{

  // 扩展的名称 (不要在名称中包含node 或者 js, 这是隐式关键字).

  // 这是通过require() 导入扩展的名称.

  "name" : "notify",

  // Version should be http://semver.org/ compliant

  "version" : "v0.1.0"

  // 这些脚本将在调用npm安装和npm卸载的时候运行.

  , "scripts" : {

  "preinstall" : "node-waf configure && node-waf build"

  , "preuninstall" : "rm -rf build/*"

  }

  // 这是构建我们扩展的相对路径.

  , "main" : "build/default/gtknotify.node"

  // 以下是可选字段:

  , "description" : "Description of the extension...."

  , "homepage" : "https://github.com/olalonde/node-notify"

  , "author" : {

  "name" : "Olivier Lalonde"

  , "email" : "olalonde@gmail.com"

  , "url" : "http://www.syskall.com/"

  }

  , "repository" : {

  "type" : "git"

  , "url" : "https://github.com/olalonde/node-notify.git"

  }

  }

  关于package.json 格式的更多细节, 可以通过 npm help json 获取文档. 注意 大多数字段都是可选的.

  你现在可以在你的顶级目录中通过运行npm install 来安装你的新的npm包了. 如果一切顺利的话, 应该可以简单的加载你的扩展 var notify = require('你的包名');. 另外一个比较有用的命令式 npm link 通过这个命令你可以创建一个到你开发目录的链接,当你的代码发生变化时不必每次都去安装/卸载.

  假设你写了一个很酷的扩展, 你可能想要在中央npm库发布到网上. 首先你要先创建一个账户:

  ?

  1$ npm adduser

  下一步, 回到你的根目录编码并且运行:

  ?

  1

  2$ npm publish

  就是这样, 你的包现在已经可以被任何人通过npm install 你的包名命令来安装了.

 

时间: 2024-08-07 16:57:24

使用Node.js为其他程序编写扩展的基本方法的相关文章

使用Node.js为其他程序编写扩展的基本方法_node.js

 准备开始 首先我们用下面的目录结构来创建一个节点通知(node-notify)文件夹.   复制代码 代码如下: . |-- build/                   # This is where our extension is built. |-- demo/ |   `-- demo.js              # This is a demo Node.js script to test our extension. |-- src/ |   `-- node_gtkno

Node.js 条形码识别程序构建思路详解_node.js

在这篇文章中,我们将展示一个非常简单的方法构建一个自定义的 Node 模块,该模块封装了Dynamsoft Barcode Reader SDK ,支持 Windows.Linux 和 OS X,同时我们将演示如何集成这块模块实现一个在线的条形码读取应用. 越来越多的 Web 开发者选择 Node 来构建网站,因为使用 JavaScript 来开发复杂的服务器端 Web 应用越来越便利.为了扩展在不同平台下的 Node 的功能,Node 允许开发者使用 C/C++ 来创建扩展. 介绍 Dynam

Node.js插件的正确编写方式_node.js

Node.js在利用JavaScript编写后端方面效果拔群,值得我们多加尝试.不过如果大家需要一些无法直接使用的功能甚至是根本无从实现的模块使用,那么能否从C/C++库当中引入此类成果呢?答案是肯定的,大家要做的就是编写一款插件,并借此在自己的JavaScript代码中使用其它代码库的资源.下面我们就一同开始今天的探询之旅. 介绍 正如Node.js在官方说明文档中所言,插件是以动态方式进行链接的共享式对象,能够将JavaScript代码与C/C++库接驳起来.这意味着我们可以引用任何来自C/

Node.js Addons翻译(C/C++扩展)_node.js

PS:请先升级Node 6.2.1,Node 升级命令 npm install -g n;n stable.NOde.js扩展是一个通过C/C++编写的动态链接库,并通过Node.js的函数require()函数加载,用起来就像使用一个普通的Node.js模块.它主要为Node与C/C++库之间提供接口. 这样,若一个方法或函数是通过Node扩展实现则变得相当复杂,涉及几个模块与接口的知识: •v8:一个实现了通过C++库实现了的javascript.V8提供了创建对象机制,回调函数等.V8AP

在Node.js应用中读写Redis数据库的简单方法

  这篇文章主要介绍了在Node.js应用中读写Redis数据库的简单方法,Redis是一个内存式高速数据库,需要的朋友可以参考下 在开始本文之前请确保安装好 Redis 和 Node.js 以及 Node.js 的 Redis 扩展 -- node_redis 首先创建一个新文件夹并新建文本文件 app.js 文件内容如下: ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 var redis = require("redis") ,

在Node.js中使用HTTP上传文件的方法

  这篇文章主要介绍了在Node.js中使用HTTP上传文件的方法,作者以windows下的visual studio作为操作node的环境,推荐阅读!需要的朋友可以参考下 开发环境 我们将使用 Visual Studio Express 2013 for Web 作为开发环境, 不过它还不能被用来做 Node.js 开发.为此我们需要安装 Node.js Tools for Visual Studio. 装好后 Visual Studio Express 2013 for Web 就会转变成一

在Node.js中使用HTTP上传文件的方法_node.js

开发环境我们将使用 Visual Studio Express 2013 for Web 作为开发环境, 不过它还不能被用来做 Node.js 开发.为此我们需要安装 Node.js Tools for Visual Studio.  装好后 Visual Studio Express 2013 for Web 就会转变成一个 Node.js IDE 环境,提供创建这个应用所需要的所有东西..而基于这里提供的指导,我们需要:     下载安装 Node.js  Windows 版,选择适用你系统

Node.js和PHP根据ip获取地理位置的方法

这篇文章主要介绍了Node.js和PHP根据ip获取地理位置的方法,通过新浪接口根据IP地址获取所在城市,需要的朋友可以参考下 一.Node.js实现代码  代码如下: var http = require('http'); var util = require('util');   /**  * 根据 ip 获取获取地址信息  */ var getIpInfo = function(ip, cb) {     var sina_server = 'http://int.dpool.sina.c

Node.js 命令行程序开发教程

一种编程语言是否易用,很大程度上,取决于开发命令行程序的能力. Node.js 作为目前最热门的开发工具之一,怎样使用它开发命令行程序,是 Web 开发者应该掌握的技能. 最近,Npm的网志有一组系列文章,我觉得写得非常好.下面就是我在它的基础上扩展的教程,应该是目前最好的解决方案了. 一.可执行脚本 我们从最简单的讲起. 首先,使用 JavaScript 语言,写一个可执行脚本 hello . #!/usr/bin/env node console.log('hello world'); 然后