Node.js C++擴展實現
因為有了Node.js,JavaScript可以被用于服務端編程。通過各種擴展,Node.js可以變得非常強大。今天分享下怎樣用C++創建Node.js擴展。
參考原文:Making Dynamsoft Barcode SDK an Addon for Node.js
搭建Nodejs開發環境
要構建擴展,需要安裝node-gyp:
npm install -g node-gyp
這個庫里面包涵了JavaScript v8引擎所需要的頭文件以及依賴庫。
創建一個C/C++文件dbr.cc以及配置文件binding.gyp。打開配置文件,在里面加入擴展名和源代碼文件:
{ "targets": [ { "target_name": "dbr", "sources": [ "dbr.cc" ] } ] }
現在就可以用來構建dbr.node,用于Node.js的動態鏈接庫,相當于DLL。在命令行中輸入:
node-gyp configure install
這行命令干了兩件事,首先生成了Visual Studio的工程文件。然后調用VS的編譯器生成了動態鏈接庫。可以看下生成的文件結構:
build / binding.sln / dbr.vcxproj / dbr.vcxproj.filters / config.gypi / Release / dbr.node / dbr.pdb / obj
更多內容可以參考官方文檔Node.js addons。
把Dynamsoft Barcode SDK封裝成Node.js擴展
接下來我們只需要使用Visual Studio來編寫代碼,構建工程就可以了。因為node-gyp在配置的時候已經把工程文件里的頭文件路徑和依賴庫路徑都加進去了,我們只需要做很小的修改。現在雙擊binding.sln導入工程。添加Dynamsoft Barcode SDK相關的頭文件路徑和庫路徑。最后添加post-build event用于拷貝DLL到生成目錄:
copy "{installation directory}\Dynamsoft\Barcode Reader 2.0 Trial\Redist\C_C++\*.dll" "$(OutDir)"
現在開始編寫C++代碼。和Java,Python類似,在編寫native代碼的時候,需要首先注冊native函數。初始化Barcode解碼接口:
void Init(Handle<Object> exports) { NODE_SET_METHOD(exports, "decodeFile", DecodeFile); } NODE_MODULE(dbr, Init)
接下來我們把獲取的數據轉換成v8數據類型。使用Local<Object>來存儲一個Barcode結果,使用Local<Array>來存儲所有的Local<Object>。最后通過回調函數把結果返回到JavaScript層。
void DecodeFile(const FunctionCallbackInfo<Value>& args) { Isolate* isolate = Isolate::GetCurrent(); HandleScope scope(isolate); // convert v8 string to char * String::Utf8Value utfStr(args[0]->ToString()); char *pFileName = *utfStr; int option_iMaxBarcodesNumPerPage = -1; int option_llBarcodeFormat = -1; pBarcodeResultArray pResults = NULL; ReaderOptions option; SetOptions(&option, option_iMaxBarcodesNumPerPage, option_llBarcodeFormat); // decode barcode image file int ret = DBR_DecodeFile( pFileName, &option, &pResults ); if (ret == DBR_OK){ int count = pResults->iBarcodeCount; pBarcodeResult* ppBarcodes = pResults->ppBarcodes; pBarcodeResult tmp = NULL; // javascript callback function Local<Function> cb = Local<Function>::Cast(args[1]); const unsigned argc = 1; // array for storing barcode results Local<Array> barcodeResults = Array::New(isolate); for (int i = 0; i < count; i++) { tmp = ppBarcodes[i]; Local<Object> result = Object::New(isolate); result->Set(String::NewFromUtf8(isolate, "format"), Number::New(isolate, tmp->llFormat)); result->Set(String::NewFromUtf8(isolate, "value"), String::NewFromUtf8(isolate, tmp->pBarcodeData)); barcodeResults->Set(Number::New(isolate, i), result); } // release memory DBR_FreeBarcodeResults(&pResults); Local<Value> argv[argc] = { barcodeResults }; cb->Call(isolate->GetCurrentContext()->Global(), argc, argv); } }
現在創建一個JavaScript腳本文件測試一下:
var dbr = require('./build/Release/dbr'); var readline = require('readline'); var rl = readline.createInterface({ input: process.stdin, output: process.stdout }); rl.question("Please input a barcode image path: ", function(answer) { // e.g. F:\git\Dynamsoft-Barcode-Reader\Images\AllSupportedBarcodeTypes.tif dbr.decodeFile( answer, function(msg){ var result = null; for (index in msg) { result = msg[index] console.log(result['format']); console.log(result['value']); console.log("##################"); } } ); rl.close(); });
最后通過命令行運行查看結果:
源碼
https://github.com/Dynamsoft/Dynamsoft-Barcode-Reader/tree/master/samples/Node.js