Recently, the company needs to make a plug-in for Node, which aims to connect a video player to the electron environment. I am really too south, where need where move 😳, don’t spit bitter water. This article covers the entire process of configuring the Node C++ addon plug-in development environment on Windows devices.

What is Node C++ plugin

The Node plugin is a dynamically linked library written in C/C++. It is loaded into the Node environment using require() and can be used like a normal Node module. The Node plug-in can be used to write high-performance C++ algorithms, or to provide interface encapsulation between the Node environment and other C/C++ libraries for mutual invocation. In the early days of Node plug-in development, the API that relied heavily on the V8 engine may have encountered the situation that the plug-in was not available after upgrading the Node version and needed to be recompiled. This is because the binary ABI interface of the V8 engine has changed due to the Node version upgrade, which makes the previously compiled Node plug-in unavailable. To address this issue, a new N-API has been released in Node 8.0. N-api is not a new plug-in writing pattern. N-api is an encapsulation of the V8 engine API, providing an external interface in the C-style API and ensuring that the interface is ABI stable. Node plug-ins written using n-API can be written once, compiled once, and run across multiple Node versions. The N-API interface is stable in 8.12.0 and later (see ABI-stables – Node) and can be safely used in production environments.

Environment to prepare

  • Install Visual Studio 2017

Here I installed Visual Studio community edition 2017 with the C++ development component selected.

  • Install Python 2.7

Download the binary installation package from the Python official website. Since the C++ library I will connect to later is x86 architecture, in order to avoid possible trouble, I choose the x86 architecture installation package for Python.

  • Install the NVM and Node

There are so many Versions of Node, which one should I use? What if I switch later? No fear, NVM is done. In github.com/coreybutler… Download the WindwOS NVM installation package to install the NVM. On the cli, enter NVM install 10.17.0 32 to install node long-term support 10.17.0 x86 and enter NVM use 10.17.0 32 to activate the node version.

  • Installation node – gyp
npm install -g node-gyp
Copy the code

Compile the Node plug-in using Node-gyp. Node-gyp is much easier now than in the early days of plug-in development when you had to configure all sorts of annoying compile and link parameters in different places.

Hello World

In github.com/nodejs/node… There are several official Node plug-in hands-on sample projects. Most of these small demos officially provide three implementations: NAN, N-API, and Node-Addon-API. Node-addon-api is a C++ wrapper around the C-form N-API, which is also ABI-compliant. I personally recommend using Node-Addon-API. NAN was an early API for writing plug-ins that needed to be used in conjunction with the V8 API and is no longer recommended.

Download and open 1_hello_world in Node-Addon-examples and open the folder using your most comfortable editor, Visual Studio Code. CD node-addon-api && NPM install && node-gyp build && NPM test in VSCode integration terminal to see the output of node plug-in hello world.

After node-gyp build, the Visual Studio project file binding. SLN will be generated in the build folder and opened with Visual Studio 2017. You can use smart code hints and completion to speed up the writing of Node plug-ins, and you can compile plug-in projects in Visual Studio. The Visual Studio project file is actually generated using the Node-gyp configure command.

Using Node-addon-API, the plug-in code is much cleaner and easier to read than using n-API directly. NODE_API_MODULE the first argument is the plug-in name and the second argument is the Init registration function. Init register function, bind Hello to the Method function.

#include <napi.h>

Napi::String Method(const Napi::CallbackInfo& info) {
  Napi::Env env = info.Env();
  return Napi::String::New(env, "world");
}

Napi::Object Init(Napi::Env env, Napi::Object exports) {
  exports.Set(Napi::String::New(env, "hello"),
              Napi::Function::New(env, Method));
  return exports;
}

NODE_API_MODULE(hello, Init)
Copy the code

On the javascript side, bindings can be used to load modules. Bindings can be used to solve the problem of finding plug-in paths because binaries are compiled to many different locations in Node plug-in history. Bindings can be used to check all possible plug-in build locations and return to the first successful loading location.

const addon = require('bindings') ('hello');
console.log(addon.hello());
Copy the code

Run this js file with Node and output world successfully

Used in the Electron project

In this simple demo, we have finished writing, compiling, loading, and running the Node plug-in. The next step is to move this into the Electron development run environment.

Two reminders:

1. Make sure NPM install electron is installed successfully. Otherwise, mistakes are confusing. For example, I encountered this error. Error: Electron failed to install correctly, please delete node_modules/electron and try installing again. And this mistake

[email protected] postinstall D:\electron\ electric-react \node_modules\electron > node install.js (node:12492) UnhandledPromiseRejectionWarning: Error: EPERM: operation not permitted, Lstat ‘C: \ Users \ befovy \ AppData \ Local \ Temp \ electron – download – Jj9PbA \ electron – v7.0.0 – win32 – ia32. Zip’ (12492) node: UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)

I was devastated to solve the above error. Error message appears to be a permission problem, I am using the administrator permission CMD still error. Someone on Github said NPM cache Clean works, but it doesn’t work either. Finally I tried to create a new computer user, restart and other methods, unexpectedly inexplicable good.

2. The Node version used must match the Node version supported by Electron.

In Electron version page electronjs.org/releases/st… You can view the Node version matched by Electron. The instructions on this page indicate that the Electron 7.0.0 version matches the Node version 12.x. So I switched Node version to 12.13.0 using NVM and Electron ran successfully.

From github.com/electron/el… Clone a electron starter. In this paper, when clone the project, the git submission record is 77D1CB4. Set up node version 12.13.0 32 as described earlier in this article, and then NPM install, NPM Start electron App will run successfully. If you know anything about Electron, you know that Electron has the main process and the Renderer process. In this article we will try to call functions from Addon in both processes.

  • The first symbolic export is to the index.js file in the Node plug-in directory.
const addon = require('bindings') ('hello');
module.exports = {
    addon: addon
}
Copy the code
  • The dependency on ADdon is introduced in Electron via NPM
"dependencies": { "hello_world": "file:.. /hello" }Copy the code

Modify package.json to introduce dependencies as relative directories.

  • Call addon functions in main.js, preload.js, renderer.js
// main.js
function createWindow () {
	/ /... . Omit context code
  mainWindow.loadFile('index.html')
  
  const {addon} = require('hello_world')
  console.log(`hello ${addon.hello()} from main.js`)
  / /... . Omit context code
}
  

// preload.js
window.addEventListener('DOMContentLoaded', () = > {/ /... . Omit context code
  const {addon} = require('hello_world')
  replaceText(`hello-world`, addon.hello())
}
                        
// renderer.js
const {addon} = require('hello_world');
console.log(`hello ${addon.hello()} from renderer.js`)
            
Copy the code
// index.html
<p> hello <span id="hello-world"></span> from preload.js. </p>
Copy the code

After adding the above code changes, NPM start opens the electron app and you can see that the call to the addon plugin function in main.js and preload.js executes successfully. Renderer.js is called incorrectly because require is not defined









In the Stack Overflow

app.on('ready', () => {
    mainWindow = new BrowserWindow({
        webPreferences: {
            nodeIntegration: true}}); });Copy the code

Renderer.js is also successfully called to the addon function after the modification





Is not over

Node Addon and project configuration code used in this article are available on github github.com/befovy/node… . This submission is exactly the same as this article and was updated on October 27, 2019.

I also planned to use the Node plugin in the Electron and React environment, but it didn’t work in a short time. Save it for later. In Electron and React projects, the Node plugin can be used in main.js and preload.js as well as in the Electron environment alone. React-related js code does not work, so the problem is complicated. It is not clear that webpack does not understand require(‘hello.node’), but it is not familiar with webpack. I’ll be back!!

I’m back. Electron + React

The following was updated on October 28, 2019.

The function in Addon can be successfully called in main.js, preload.js and React code app.js in the Electron and React environment. After discussion with colleagues, preload.js is executed in the same process as app.js and can pass parameters.

Modify the code as follows:

// preload.js
global.addon = require('hello_world')
Copy the code

// App.js
const {addon} = window.addon;

function App() {
  return (
    <p>Learn React, hello {addon.hello()} </p>
  );
}
Copy the code

We’re really done here. All code is viewed in the Github repository above, and the commit hash is b841f0d.