やりたかったこと
Electron で Windows アプリを作り、 Electron 側から既成の dll が使えることを確認することです。
ゴールとしては、
- C++ で "Hello, world!" という文字列を返却する HelloWorld 関数を作る
- 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↓
{
"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 を手に入れます。
#include <iostream>
extern "C" __declspec(dllexport) const char * HelloWorld();
#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 で勝手に作られるファイル
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()
}
})
<!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 .
ちゃんと狙い通りの 【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 // ←代わりにこっちを実行する