electron tutorial (III): using dll ffi-napi introduced in C ++

My electron tutorial series

Create electron and installation projects: electron tutorial (a)

electron tutorial (b): http server, ws server, process management

electron tutorial (III): using dll ffi-napi introduced in C ++

 

introduction

 

This one will explain how node.js + electron environment, the use of node-ffi/ffi-napiwritten call the C / C ++ dynamic-link libraries (ie dll), implementation calls C / C ++ code.

This tutorial is for electron 4.x-6.x version.

Such as electron 4.2.10 version, electron 5.0.6 version, electron 6.0.10 version.

 

ffi

 

To achieve this function, plug mainly used ffi.

node-ffi is a pure JavaScript to load plug-ins and calls Node.js dynamic library. It can be used to create a binding local DLL library without having to write any C ++ code. At the same time it handles type conversion across JavaScript and C.

node-ffiConnecting the C code and JS code, to complete the call through the shared memory, and the internal passed ref, ref-arrayand ref-structto achieve conversion.

 

Installation ffi-napi

 

ffi-napiIt is the author (node-ffi-napi) in accordance with node-ffiand to modify release npm仓库, you can install directly via npm, and support for node.js 12 high electron version.

ffi-napiFor details, see: FFI-NAPI's github page

node-ffiFfi is the official version, but can not be used in our projects, if you are interested in it cause of the failure, I wrote in the last section of this article.

 

1. Deploy node.js + electron environment

node.js + electron Course (a): Installation and electron nodejs

 

2. Installation ffi-napi

Execution command:

yarn add ffi-napi

 

Use ffi-napi

 

In main.jsadd the following code:

const ffi = require('ffi-napi');
/**
 * 先定义一个函数, 用来在窗口中显示字符
 * @param {String} text
 * @return {*} none
 */
function showText(text) {
  return new Buffer(text, 'ucs2').toString('binary');
};
// 通过ffi加载user32.dll
const myUser32 = new ffi.Library('user32', {
  'MessageBoxW': // 声明这个dll中的一个函数
    [
      'int32', ['int32', 'string', 'string', 'int32'], // 用json的格式罗列其返回类型和参数类型
    ],
});

// 调用user32.dll中的MessageBoxW()函数, 弹出一个对话框
const isOk = myUser32.MessageBoxW(
    0, showText('I am Node.JS!'), showText('Hello, World!'), 1
);
console.log(isOk);

This code, call the main windows of user32.dll, concrete steps are written in the comments of the code.

starting program:

Elevation start

Pop appearance, Hello World!

 

Generate their own a dll

 

0. The first thing to be clear is this:

ffi only accept pure C functions, rather, it is compiled in accordance with the standard C function

It says the following specific reasons:

When dll introduced by ffi, we are such a declaration:

const myUser32 = new ffi.Library('user32', {
  'MessageBoxW': // 声明这个dll中的一个函数
    [
      'int32', ['int32', 'string', 'string', 'int32'], // 用json的格式罗列其返回类型和参数类型
    ],
});

In the user32.dllmiddle, looking for a named MessageBoxWfunction.

However, C and C ++ function name is different, I mean the name of the function compiled

For C, the function int func(int n)will be compiled into a similar _funcsuch names.

For C ++, the function int func(int n)will be compiled into a similar ?func@@YAHH@Zsuch names.

The same is C ++, the function int func(int double)will be compiled into a similar ?func@@YAHN@Zsuch names (a different and on).

Name contains more information, such as:

Drawing mode parameters
return type of
the parameter type and number

This is because C ++ has 函数重载characteristics, although the function is named func, but int func(int n)and int func(int double)is an entirely different function, the compiler to distinguish them by assigning them different names.

If you're interested, here's a more detailed explanation

Back ffi, it looks for the name of the function in the dll in time, it is to find the C-style.

So if your function in C ++ compiler, ffl in this dll can not find this function in error LINK 126!

1. Create a project

Using VS to create an empty C ++ project to project into to name myAddDll example.

Of course, you can also create a dynamic link library DLL directly.

 

2. modify the configuration of type .dll dynamic libraries


Figure:

In the project configuration, select a dynamic library .dll

Make sure you configure the Debug and Release, while ensuring that you generated in the x64 environment.

 

3. function declaration

创建一个myAdd.h头文件

声明一个函数:

extern "C"
{
    __declspec(dllexport) int funAdd(int a, int b);
}

extern "C"意味着:

被 extern "C" 修饰的变量和函数是按照 C 语言方式编译和链接的

__declspec(dllexport)意味着:

__declspec(dllexport)用于Windows中的动态库中,声明导出函数、类、对象等供外面调用,省略给出.def文件。即将函数、类等声明为导出函数,供其它程序调用,作为动态库的对外接口函数、类等。

 

4. 函数定义

创建一个myAdd.cpp文件

定义funAdd函数:

#include "myAdd.h"
int funAdd(int a, int b)
{
  return (a + b);
}

函数的内容很简单, 接受两个int类型参数, 返回它们的和.

 

5. 生成dll

右键项目选择生成即可, 生成的myAddDll.dll位于项目目录下的x64/Debug中.

(根据你项目的配置去找, x64或x86, Debug或Release)

 

6. 测试dll

myAddDll.dll拷贝至你的electron项目的根目录下的dll文件夹内

在main.js中添加如下代码:

const ffi = require('ffi-napi'); // 如果前面已经定义过ffi, 就注释掉这一行
// 通过ffi加载myAddDll.dll
const myAddDll = new ffi.Library('./dll/myAddDll', {
  'funAdd': // 声明这个dll中的一个函数
    [
      'int', ['int', 'int'], // 用json的格式罗列其返回类型和参数类型
    ],
});

// 调用函数, 参数1和2, 将返回值直接打印出来, 预计为3
const result = myAddDll.funAdd(1, 2);
console.log(`the result of 1 + 2 is: `+ result);

这段代码中, 主要调用了myAddDll.dll中的int funAdd(int, int), 具体的步骤都写在了代码的注释中.

启动程序:

npm start

查看cmd中的日志:

the result of 1 + 2 is: 3

 

6.1 可能的错误

LINK 126

这个错误, 意味者electron无法使用你的dll.

在这行代码中

const myAddDll = new ffi.Library('./dll/myAddDll', {

ffi.Library的第一个参数, 不光指定了dll的名字, 还指定了dll的路径.

出现LINK 126有两个常见原因:

  1. 没有这个目录, 或这个目录下没有myAddDll.dll

  2. myAddDll.dll还依赖了其他的一些dll, 但是electron无法找到这个dll.

LINK 127

出现LINK 127的可能原因:

  1. electron找到了你的dll, 但是在dll中找不到你声名的函数(funAdd).这通常是由于函数名字错误, 或者是返回值类型/参数的个数及类型不一致导致的.

node-ffi为什么会安装失败

 

如果我们按照node-ffi的github链接中介绍的方法来安装ffi, 即

npm install ffi

然后尝试在main.js中加上一句代码来导入这个模块,

const ffi = require('ffi');

运行一下

npm start

你会得到, 一个错误!

仔细看看提示:

The module xxxxxx was compiled against a different Node.js version using
NODE_MODULE_VERSION 69. This version of Node.js requires
NODE_MODULE_VERSION 73

简单地说:

这个模块是用一个NODE_MODULE_VERSION 69的node.js版本进行编译的,
而当前的版本的Node.js需要NODE_MODULE_VERSION 73(来进行编译).

那么NODE_MODULE_VERSION是什么意思? 后面的数字又是什么意思?

我们在这里可以查询到, 各个NODE_MUODULE_VERSION对应的node.js版本或electron版本, 也叫做node_abi.

再翻译一下错误提示:

这个模块是用一个electron 4.0.4 版本进行编译的,
而当前的版本需要electron 6(来进行编译).

目前为止, 我们的问题解决方案:

重新编译.

重新编译, 指定electron版本, 执行指令:

npm rebuild --runtime=electron --target=6.0.10 --disturl=https://atom.io/download/atom-shell --abi=73

注: 从https://atom.io/download/atom-shell下载会比较慢, 请自备梯子, 或者使用淘宝库来下载.

日志中打印出了多条error, 原因是:

node-ffi里面会调用v8或其他依赖模块的接口, 而这些接口已经更新了, 有的接口改了名字, 有的接口改了参数数量. 但是node-ffi的调用接口语句并没有更新, 所以编译不过.

进一步的问题解决方案:

修改node-ffi的代码, 以适应新版本的v8, 和其他依赖模块.

一般而言, 我建议翻阅github中该项目的的issues, 在关于编译和electron的话题中, 你会找到其他人已经修改好的代码, 通常是一个该项目的fork版本. 你需要下载这个fork版本的源码, 将它拷贝至你的项目node_modules文件夹中, 使用上面的编译指令编译安装.

而前文介绍的ffi-napi是一个特殊情况, 作者不光修改了node-ffi. 还把它修改后的代码上传到了npm仓库, 所以我们可以通过npm install ffi-napi来进行安装, 不必按照下载-拷贝-编译的流程来安装它.

Guess you like

Origin www.cnblogs.com/silenzio/p/11606389.html