はじめに
こんにちは。イメージ・マジックの矢野です。入社からもうすぐ1か月です。
Node.jsのアドオンを、何らかの理由でネイティブのコードで書かなければならないことがあります。Node.jsのエンジンであるV8のお作法に則って書くことになりますが、V8がバージョンアップして仕様が変われば、そのお作法も変わります。お作法が変わればアドオンの書き方も変えなければならず、毎度毎度書き直すのは大変。そこで、Node.jsのネイティブなアドオンの実装をイイ感じにラップして抽象化してくれる、NAN(Native Abstractions for Node.js)というものがつくられたとのこと(READMEの冒頭を読むと、クスリとさせられます)。もちろんNANを使わなくてむ作れるわけですが、せっかく便利なものがあるのですから、使わない手はありません。
準備
Node.jsのアドオンをつくるのですから、当然Node.jsはインストールされていないといけません。
NANはnode-gypでビルドするため、node-gypもインストールしておかなければいけません。さらにgypはPythonスクリプトなので、Pythonが入っていなければインストールしておかなければなりません。
ネイティブコードをビルドするために、コンパイラなども必要です。今回はWindowsを使って開発しているので、Visual Studio 2017をインストールしておきます。
作成
準備ができたところで、作業用のディレクトリを作成します。
mkdir hoge cd hoge
NANをインストールします。
npm install --save nan
binding.gyp
を作成します。このテキストファイルには、アドオンの構成を記述します。ネイティブならではのアドオンをばっちり作り込むのはそれなりに大変なので、今回はOSのバージョンを取得するだけの簡単なものにしようと思います。
{ "targets": [ { "target_name": "getosversion", "sources": [ "src/addon.cpp", ], "include_dirs": [ "<!(node -e \"require('nan')\")", ], } ] }
ソースファイルを格納するディレクトリ(src)をつくり、ソースファイルを形だけでも(空ファイルでOK)つくっておきます(src/addon.cpp)。
node-gyp
でconfigure
すると、上記の構成に従ってVisual StudioのC++プロジェクトやソリューションを生成してくれます。
node-gyp configure
build
ディレクトリの下にいろいろファイルができています。
│ binding.gyp │ ├─build │ binding.sln │ config.gypi │ getosversion.vcxproj │ getosversion.vcxproj.filters │ └─src addon.cpp
binding.sln
を開くと、Visual Studioが起動しますので、addon.cppの中身を書いていきます。NANの恩恵を受けるには、ヘッダファイルnan.h
をincludeしておく必要があります。
Win32 APIのGetVersion()
を呼び出して、OSのバージョン情報を取得してみます。ちなみにこの関数は現在「非推奨」になっていますが、Win32がメインの記事ではないので、そのまま使ってしまいます(業務ではこんなことしませんよ)。
#include <sstream> #include <nan.h> NAN_METHOD(GetOsVersion) { DWORD dwVersion = GetVersion(); DWORD dwMajorVersion = (DWORD)(LOBYTE(LOWORD(dwVersion))); DWORD dwMinorVersion = (DWORD)(HIBYTE(LOWORD(dwVersion))); DWORD dwBuild = 0; if (dwVersion < 0x80000000) { dwBuild = (DWORD)(HIWORD(dwVersion)); } std::stringstream ss; ss << dwMajorVersion << "." << dwMinorVersion << "." << dwBuild; info.GetReturnValue().Set(Nan::New<v8::String>(ss.str()).ToLocalChecked()); } NAN_MODULE_INIT(init) { Nan::SetMethod(target, "GetOsVersion", GetOsVersion); } NODE_MODULE(getosversion, init)
ビルドします。
node-gyp build
もろもろビルドされます。肝心のアドオンは、build/Release/getosversion.node
として生成されます。
│ binding.gyp │ ├─build │ │ binding.sln │ │ config.gypi │ │ getosversion.vcxproj │ │ getosversion.vcxproj.filters │ │ │ └─Release │ │ getosversion.exp │ │ getosversion.iobj │ │ getosversion.ipdb │ │ getosversion.lib │ │ getosversion.map │ │ getosversion.node │ │ getosversion.pdb │ │ │ └─obj │ └─getosversion │ │ addon.obj │ │ vc141.pdb │ │ win_delay_load_hook.obj │ │ │ └─getosversion.tlog │ ... (省略) ... │ └─src addon.cpp
これを実際に動かしてみましょう。テスト用コードをこんな感じで書いてみます(test.js
)。
var addon = require('./build/Release/getosversion'); var version = addon.GetOsVersion(); console.log(result);
node
コマンドから実行してみると、それらしい値が取れました。
node test.js 10.0.16299