19/07/01 Qiitaに画像ファイルがアップロードしにくい(やたら時間がかかってる)ので、あとでアップロードします。
今回のコンセプト
- C++部分はElectronと連動しながらVisual Studio 2017でデバッグする。(C++11に対応していたら別バージョンでも使えるらしい)
- node.jsはnodist仮想環境を使う(node.jsのバージョンが合うなら他の方法でも可能)
- ビルド時にpython2.7使うのでAnacondaの仮想環境を使う(python2.7が動けば他の方法でも可)
- node.jsパッケージはプロジェクトディレクトリのnode_modulesに保存して、そこから使う。
-
electron-rebuild
は使わずnode-gyp
を使う。 - Electronは5.0.5(新しいバージョンでやってみたいだけ)
- 作りやすく、やり直しやすい環境構築を目指す。
開発環境
C++で開発する2種類の方法
native addon
node-gypを使ってC++をビルド。こちらを使う。
ffi
npm install ffi
でインストールする方法。
この方法は、かなり制限(WinではElectronがバージョン3(nodeのバージョンのため)までしか対応してないとか、エラー地獄にあったりとか)があるので今回は使用しない。
nodistの設定
今回はElectron5.0.5を使うのでnode.js12以上が必要です。
とりあえず以下を実行して使いたいバージョンを用意。
Electronに必要な対応するnode.jsのバージョンはRelease noteに書いてあります。
rem 使用可能なnodeバージョン一覧
nodist dist
rem 使いやすそうなnode.jsのバージョンを選択してダウンロード
nodist + 12.4.0
rem ダウンロード済みの中で使用したいバージョンを指定
nodist use 12.4.0
rem 恒久的に今後同じバージョンを指定する場合はglobalで設定する
nodist global 12.4.0
Anacondaの設定
node-gyp
やelectron-rebuild
とかのライブラリでpython2.7を使うのでAnacondaにインストール
rem py27という名前で作成
conda create -n py27 python=2.7
プロジェクトの作成
普通にフォルダ作ってるだけです。
rem 例としてexampleという名前
mkdir examples
cd examples
プロジェクト作成
package.json作成
rem conda でpython27に変更
activate py27
rem nodistでnode.jsのバージョンを指定
nodist use 12.4.0
rem node初期化
rem 対話型で内容を設定
npm init
Electronとnode-gypをインストール
rem プロジェクトに保存してそこから実行する予定
npm install --save-dev electron@5.0.5
npm install --save-dev node-gyp@5.0.1
なんかエラー出たときは、package-lock.jsonを削除するとうまくいくかも
ファイルを用意
Hello Worldします。
node/test/addons/hello-world at master · nodejs/node · GitHub
スクリプトはこちらを参考にしました。
node-gypまたはnanを使ってnative addonを作る方法 - DJ lemon-Sour's diary (prod.hisasann)
HTML
ファイル名はindex.html
<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><title>Example</title></head>
<body>
<div id='test'></div>
<button onclick="testFunc()">test button</button>
<script>
function testFunc(){
var remote = require('electron').remote;
var testC = require('./build/Debug/testFunction');
document.getElementById('test').innerText = "hello " + testC.hello();
}
</script>
</body>
</html>
Electron用JS
ファイル名はindex.js
"use strict";
const electron = require("electron");
const app = electron.app;
const BrowserWindow = electron.BrowserWindow;
let mainWindow;
// 閉じたときのイベント
app.on('window-all-closed', function() {
if (process.platform != 'darwin') {
app.quit();
}
});
// 初期化後
app.on('ready', function() {
mainWindow = new BrowserWindow({
width: 800, height: 600,
webPreferences: {
// Electron5以降ではこれがFalseになっていて、index.html側でrequireが使えないのでTrueにする(後述)
nodeIntegration: true
}
});
mainWindow.loadURL('file://' + __dirname + '/index.html');
mainWindow.on('closed', function() {
mainWindow = null;
});
});
注意
今回は簡単に説明したいのでnodeIntegrationという値をtrueにしてますが、セキュリティ的に宜しく無いらしいので、適当に変更してください。
Electron Webviewのセキュリティで注意すべきこと - Qiita
package.json
package.jsonは先の初期化時に生成されているはずですが、"main": "index.js",
となっていない場合(index.jsがメインファイルでない場合)は、そのように直しておく。
binding.gyp
node-gypが使用するビルド用の設定ファイル。
ファイル名はbinding.gyp
で固定
{
"targets": [
{
"target_name": "testFunction",
"sources": [
"test.cc"
]
},
]
}
test.cc
#include <node.h>
#include <v8.h>
void Method(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::Isolate* isolate = args.GetIsolate();
v8::HandleScope scope(isolate);
args.GetReturnValue().Set(v8::String::NewFromUtf8(isolate, "world"));
}
void init(v8::Local<v8::Object> exports) {
NODE_SET_METHOD(exports, "hello", Method);
}
NODE_MODULE(binding, init);
ビルド
ファイルができたらビルドしてみる。
Using Native Node Modules | Electron
electron-rebuild
を使う方法もあるが、環境によってはうまく使いたいnode-gypのバージョンで使えなかったのでnode-gypでビルドする。
rem conda でpython27に変更
activate py27
rem nodistでnode.jsのバージョンを指定
nodist use 12.4.0
rem プロジェクトディレクトリに保存したnode_modules内のnode-gypを使ってビルド
.\node_modules\.bin\node-gyp.cmd rebuild --target=5.0.5 --arch=x64 --msvs_version=2017 --dist-url=https://electronjs.org/headers --debug
引数は以下
引数 | 値 | 内容 |
---|---|---|
--target | 5.0.5 | Electronのバージョン |
--arch=x64 | x64 | 32bitか64bitか |
--msvs_version | 2017 | Visual Studioのバージョン |
--dist-url | https://electronjs.org/headers |
この引数を加えることでElectronのビルドであることをnode-gypに伝える |
--debug | デバッグでビルド |
実行
以下で実行。
ウィンドウが表示されて、ボタンを押したらHelloWorldが表示されればOK
.\node_modules\.bin\electron .
デバッグ
Visual Studio でデバッグしたいのでデバッグ用のプロジェクトを新たに作成します。
Visual Studio 2017で新しく「空のプロジェクト」を作成します。
対象のファイルを追加
プロジェクト名を右クリック->追加->既存の項目(G)...を選択して、デバッグの対象にしたいC++ファイルtest.cc
を選択して追加。
追加されたファイルを右クリックしてプロパティを開く。
構成プロパティ->全般->ビルドから除外
を「はい」に設定。
デバッグ用のコマンドを設定
プロジェクトのプロパティ->構成プロパティ->デバッグ->コマンド
$(ProjectDir)..\..\node_modules\.bin\electron.cmd
同じく、コマンド引数
$(ProjectDir)..\..\.
に設定。
実行
設定後、普通にデバッグ->デバッグの開始を選択か、「ローカル Windows デバッガー」ボタンをクリック
アタッチ
デバッグ->プロセスにアタッチ
を選択して、electron
という項目を探して、「タイトル」が空白のものを選択してアタッチする。(2個項目があるが、恐らくUIDが後の方だと思うが、シフトキーを押しながら複数選択のほうが確実かも)
ソリューションエクスプローラーのtestFunction/test.cc
のMethod
関数のどこかにブレークポイントを設定して、Electron上のボタンを押すと、そのブレークポイントで停止します。これでデバッグができるようになりました。
アタッチについて
デバッグ実行ごとに上記アタッチの作業が必要になる。
node_modules\electron\dist\electron.exe
をデバッグ時に自動的にアタッチできればもう少し楽できるかも。
その他設定
絶対ではないけど、やっておくと便利程度
デバッグ時にビルドも
今回のデバッグの設定はビルドをしていない。
ビルドもしておきたい場合は以下のようなバッチファイルをbuild.cmd
という名前で用意して、
cd %~dp0
call activate py27
call nodist use 12.4.0
node_modules\.bin\node-gyp.cmd rebuild --target=5.0.5 --arch=x64 --msvs_version=2017 --dist-url=https://electronjs.org/headers --debug
ビルド前コマンドに以下のような内容を書いておけばビルドもされる。
..\..\build.cmd
説明
Electronとnode.jsとNODE_MODULE_VERSIONについて
Electronの指定をしないで普通のnode.jsでnode-gypを使ってビルドすると、NODE_MODULE_VERSION
が違う旨のエラーが表示される。
NODE_MODULE_VERSION
はABI(アプリケーションバイナリインタフェース)のバージョン番号で、普通のnode.jsとelectronと違うものが振られており、たとえ同じnode.jsとnpmのバージョンを使用してnode-gypでビルドしても上手く動作してくれません。(どこかにコレではないリストでElectronも含めたNODE_MODULE_VERSION一覧表があったはずだがURLを忘れた)
Electron から native module を使うときの NODE_MODULE_VERSION のエラー - 理系学生日記
参考
Using Native Node Modules | Electron
Electron から native module を使うときの NODE_MODULE_VERSION のエラー - 理系学生日記
その他参考
Electronアプリを公開するまでにやったことノウハウまとめ - Qiita
とはいえ
デバッグ毎にアタッチするのはめんどくさいので、C++側はCLIとかで別途デバッグして、UI部分との結合用に使う程度が良いかも。