28
33

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Electron で Windows アプリを作ろうとして node-ffi でハマった話

Posted at

やりたかったこと

Electron で Windows アプリを作り、 Electron 側から既成の dll が使えることを確認することです。

ゴールとしては、

  1. C++ で "Hello, world!" という文字列を返却する HelloWorld 関数を作る
  2. Electron(JavaScript)側から HelloWorld 関数を呼び出し、alert で表示する

というものです。

ハマったポイント

npm で node-ffi をインストール後、JavaScript 側で require('ffi') をやるとエラーが発生します。これがなかなか解消できず、ハマりました。

↓こういうエラー

Uncaught Error: A dynamic link
library (DLL) initialization routine failed.

厄介なのは、JavaScript として、【require('ffi')】を呼び出した段階でエラーになってしまうこと。なぜなら、ffi を npm でインストールしただけ だから。
こうなると、インストールしただけではダメなのか? という疑問が出てくる。

いろいろ調べると・・・

npm でインストールしただけでは使えなさそう な臭いがプンプンしてきます。
で、だいたい出てくる情報は、

Electron で使っている V8 エンジンが Node.js の V8 エンジンと違うので、Electron 用にパッケージをリビルドしないといけない

という情報です。
で、それについて調べると出てくるのが、

electron-rebuild

 
です。

StackOverflow でも「解決策見つけたよ」と言っていた人が ここ を紹介していて、
ページの中段辺りの Installing modules and rebuilding for Electron で思いっきり書いてある、と。

そうなると期待を膨らませて実行するわけですよ。

$ npm install electron-rebuild
$ ./node_modules/.bin/electron-rebuild
$ レRebuild Complete

よし!リビルド完了!(★注目★)

$ ./node_modules/.bin/electron .

Electron 実行!

Uncaught Error: A dynamic link
library (DLL) initialization routine failed.

ぐぬぬ!

割と最近の記事で、↓の記事の人も electron-rebuild やっている・・・。うーん・・・。
【Electron】Felica連携デスクトップアプリケーションの開発

というわけで、真にハマったところは、この electron-rebuild なのです。

そして解決へ

こういうダダハマリした場合って、けっこう手前(前提的なところ)に落とし穴があって、
今回もその例から外れることなく、結果から見ると非常に単純な理由でした。

【(★注目★)】と書いたところの electron-rebuild

こいつ、リビルドしてないぞ!

リビルドって言ってる割には一瞬で終わるのですよ、electron-rebuild コマンド。

で、いろいろ試した結果、どうすれば electron-rebuild でリビルドしてくれるのかというと、

npm でインストールするときに -D(--save-dev) を付けない

ということです。
そして残念なことに

-g でのグローバルインストールで統一してもダメ

です。(一番解決できそうな組み合わせなのに・・・)

で、結論です。

★ node-ffi だけは npm install で -g, -D(--save-dev) を付けない ★

です。

実際にやったことなど

まず、状況の確認

  • Windows 10 Pro 1803(April 2018 Update)
  • Visual Studio 2017 Community(VC++インストール済み)
  • Node.js 8.11.4(64ビット版)

↓Node バージョンやパッケージの状態↓

$ node -v
v8.11.4
$ npm -v
6.4.1
$ npm list -g --depth=0
npm
$ npm list --depth=0
なし

パッケージをインストールする

$ npm i electron
$ npm i electron-rebuild
$ npm i ffi

↓インストールした結果の package.json↓

package.json
{
  "name": "electron_ffi_sample",
  "main": "main.js",
  "author": "odorry",
  "scripts": {
    "start": "node_modules/.bin/electron ."
  },
  "dependencies": {
    "electron": "^2.0.9",
    "electron-rebuild": "^1.8.2",
    "ffi": "^2.2.0"
  }
}

npm i(install) でインストールしたので、
dependencies のところにインストールしたものが書かれている。

Electron の V8 エンジン用にリビルドする

これですね。肝は。
このコマンドで、リビルドにある程度の時間がかかれば OK です。
(5 〜 10 秒くらいで、少なくとも一瞬ではない。)

$ ./node_modules/.bin/electron-rebuild
レ Rebuild Complete

Visual Studio の VC++ で dllSample.dll を作成する

Visual Studio の使い方になってしまうので、詳細は割愛しますが、
Visual Studio でビルドして DllSample.dll を手に入れます。

DllSample.h
#include <iostream>

extern "C" __declspec(dllexport) const char * HelloWorld();
DllSample.cpp
#include "stdafx.h"
#include "DllSample.h"

const char * HelloWorld()
{
	const char *cstring = "Hello, World!";
	return cstring;
}

これをビルドして DllSample.dll を手に入れてください。

Electron アプリ(html)側を用意する

ファイル構造はこのような感じです

任意のフォルダ
┣ node_modules      // npm install でインストールされたパッケージ
┣ DllSample.dll     // Visual Studio 側で作成した C++ の DLL
┣ index.html        // Electron アプリの最初の画面
┣ main.js           // Electron アプリの起動スクリプト
┣ package.json      // Node パッケージ用のファイル
┗ package-lock.json // npm install で勝手に作られるファイル
main.js
const {app, BrowserWindow} = require('electron')

let win

app.on('ready', () => {
  win = new BrowserWindow({width: 800, height: 600})

  win.loadURL('file://' + __dirname + '/index.html')

  win.on('closed', () => {
    win = null
  })
})

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

app.on('activate', () => {
  if (win === null) {
    createWindow()
  }
})
index.html
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>DLL呼び出しサンプル</title>
</head>
<body>
    <script>
        var ffi = require('ffi');

        var dllSamplePath = "./DllSample.dll";
        var dllSample = ffi.Library(dllSamplePath, {
            'HelloWorld': ['string', []]
        });

        var result = dllSample.HelloWorld();
        alert(result);
    </script>
</body>
</html>

Electron アプリとして実行してみる

$ ./node_modules/.bin/electron .

↓実行結果です↓
electron_ffi_sample.png

ちゃんと狙い通りの 【Hello, world!】 が表示されています。

まとめ

  • ffi をインストールするときは -g, -D(--save-dev) を付けない
  • ffi をインストール後に electron-rebuild でリビルドする

おまけ

NAN の場合も同様で electron-rebuild によるリビルドが必要です。
その際、node-gyp で clean, configure を行い、
その後に electron-rebuild すれば OK です。

$ ./node_modules/.bin/node-gyp clean
$ ./node_modules/.bin/node-gyp configure
$ #./node_modules/.bin/node-gyp build // ←これをやらずに
$ ./node_modules/.bin/electron-rebuild // ←代わりにこっちを実行する
28
33
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
28
33

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?