node-ffi เรียก functions ใน dll ด้วย JavaScript กันตรงๆ

จากความเดิมตอนที่แล้วผมเคยเขียนเรื่องการสร้าง Node.js Module ด้วย C++ มาทีนึงแล้ว แต่ถึงอย่างนั้นถ้าเรา
– มี C++ Library อยู่แล้วล่ะ
– หรืออยากทำให้ Node สามารถเรียกใช้งานพวก System API โดยที่ไม่อยากศึกษาการเขียน Node Module แบบ Native
ผมก็ไปเจอว่ามี Community ที่ได้พัฒนา Framework มาตัวนึงซึ่งวันนี้เราจะมาพูดถึงกัน นั่นก็คือ Node-ffi ซึ่งย่อมาจาก Foreign Function Interface

Node-ffi นั้นเป็น Module ที่ทำหน้าที่เป็นตัวกลางในการทำการติดต่อระหว่าง JavaScript Code ไปที่ C++ Library โดยที่เราไม่ต้องเขียน Native Module เอง ประมาณรูป Diagram ด้านล่างครับ

ffi.png

เรามาลองดูการใช้งานกันดีกว่าครับ

ก่อนอื่นเรามี C++ Library ที่เป็น DLL ที่ต้องการจะเรียก ซึ่งผมลองเขียนมาแบบง่ายๆคือ dll ที่มี function ในการ ดึงค่า Version ของ OS

extern "C" DLLIMPORT int getVersion()
{
 DWORD dwVer = ::GetVersion();
 DWORD majorVer = (DWORD)(LOBYTE(LOWORD(dwVer)));
 return majorVer;
};

ซึ่ง API ::GetVersion นั้นเป็นของ Windows SDK ครับ ซึ่งสำหรับฝั่ง C++ ผมได้ทำการ  Compile ออกมาเป็น dll ชื่อ dev.dll

ทีนี้มาลองดูส่วนที่สำคัญของเราก็คือฝั่ง JavaScript

var ffi = require('ffi');

var devModule = ffi.Library('dev', {
 'getVersion': [ 'int', [] ]
});
console.log('Windows version : ' + devModule.getVersion());

จาก Code นี้ก็มี 3 ส่วนหลักๆครับ

  1. require module ‘ffi’
  2. ประกาศการทำ function binding
    ffi.Library('dev', {
      'getVersion': [ 'int', [] ]

    2.1 ทำการ binding กับไฟล์ library ชื่อ dev.dll
    2.2 ทำการ binding กับ function ชื่อ getVersion โดยมี ‘int’ เป็น Type ของ Return Value
    2.3 Function getVersion นี้ไม่มี parameter เลยใส่เป็น []

  3. เรียกใช้ ffi ด้วย  devModule.getVersion()

ผลของการรันก็เป็นดังนี้ครับคือได้ค่า ’10’ มาจาก getVersion

ffi_run.png

นอกจากนี้เรายังสามารถประกาศการทำ binding ทีละหลายๆ function ก็ได้เหมือนกันครับ(ขออนุญาติไม่โชว์ Code ฝั่ง C++ ที่เขียนเพิ่มเพื่อดึงค่า Minor,Build Version) เช่น

var devModule = ffi.Library('dev', {
 'getVersion': [ 'int', [] ] ,
 'getMinorVersion' : [ 'int', [] ],
 'getBuildVersion' : [ 'int', [] ]
});
console.log('Windows version : ' + devModule.getVersion());
console.log('Minor version : ' + devModule.getMinorVersion());
console.log('Build version : ' + devModule.getBuildVersion());

ผลการรันก็ดูดีครับและตรงกับ System Info จริงๆนะ 🙂

ffi_run2.png

สรุป : node-ffi น่าจะเป็นอีกหนึ่งทางเลือกสำหรับคนที่เขียน Node.Js ด้วย Context บางอย่างที่ต้องติดต่อกับ System หรือ OS ระดับ Low Level (เหมือนผม..) โดยที่เรานั้นไม่ต้องมาการเรียนรู้การเขียน Node Module แบบ Native ที่อาจจะมีความซับซ้อนกว่าทั้งในแง่การทำ Mapping พวก Data Types รวมถึงการมานั่งศึกษา V8 API  ซึ่งผมคิดว่า node-ffi อาจจะมีผลกระทบกับ Performance ในการ call function บ้าง แต่ผมก็ยังไม่ได้ลองวัด ทั้งนี้ทั้งนั้นใครมีข้อมูลเพิ่มก็แชร์กันได้ครับ