WebView2を使ったTablacus Explorer Blink版では2つのJavaScriptエンジンで動作しています。
- UI用JavaScript (Blink/V8)
- 同期用JScript (Edge legacy/Chakra)
1.のBlinkのJavaScriptには外部にリアルタイムに戻り値を返せないという問題があるので2.のEdge用のChakraエンジンを併用しています。
スクリプトファイルの割り振り
- scriptフォルダに入っているファイルでは 「consts.js」「common.js」はUI用JavaScript、同期用JScriptの両方で使用します。 「ui.js」「index.js」「options.js」はUI用JavaScriptのみで使用します。 「sync.js」「sync1.js」「syncb.js」は同期用JScriptのみで使用します。 「background.js」はマルチプロセスのスクリプト実行時に同期用JScriptで使用します。 「threads.js」はマルチスレッドのスクリプト実行時に同期用JScriptで使用します。 「update.js」はTablacus Explorer更新時にWSHで使用します。
※ sync*.js にありUI用JavaScript側にない関数はコピーされます。
- 個々のアドオンでは 「script.js」「options.js」はUI用JavaScriptで使用しています。 「sync.js」はUI用JavaScriptで同期用JScriptで使用しています。
2つのJavaScriptエンジンの使い方
UI用JavaScript (Blink/V8)
タイプがJavaScript
の場合に実行されます。
Tablacus Explorerのオブジェクトのプロパティやメソッド、同期用JScriptの関数の戻り値などは非同期のPromiseオブジェクトで返されます。
基本的にawait
で受け取ってください。また、関数内でawait
を使う場合はasync function () {'や'async () => {
みたいに非同期関数にしてください。
const FV = await GetFolderView();
wsh.Popup(await FV.FolderItem.Path);
※GetFolderView
は同期用JScriptで現在開いているフォルダビューを取得します。
下のFV.FolderItem.Path
の様にオブジェクトのプロパティのプロパティを受け取る場合やオブジェクトのプロパティのメソッドの戻り値を取得する場合は前に一つawait
があれば良いようです。
wsh.Popup(await (await FV.FolderItem).Path);
みたいに書くこともできます。
同期用JScriptのwindowオブジェクトは変数の$
に入っています。
UI用JavaScriptを読み込む場合
importScript("options.js");
同期用JScriptを読み込む場合
$.importScript("sync.js");
メニューなどが開いている場合はUI用JavaScriptは停止しているようです。メニュー中のスクリプトは同期用JScriptを使用してください。
同期用JScript (Edge legacy/Chakra)
タイプがJScript
の場合に実行されます。await
などが不要なので標準のTrident版と互換性が高いです。
同期用JScriptからUI用JavaScriptは直接呼び出すとなぜかエラーが出るのでInvokeUI
関数かapi.Invoke(
を使います。
InvokeUI("Addons.FilterBar.Exec", [Ctrl, pt]);
api.Invoke(UI.Addons.FolderListMenu, [oMenu, items, pt]);
引数は2番目の配列として渡します。
UI用JavaScriptからの戻り値は必ずundefined
になります。値を返したい場合はコールバック関数を使います。
同期用JScriptからHTMLの部品にはアクセスできません。エラーが出ます。
const el = document.getElementById("○○");
上記の様にHTMLを操作する場合はUI用JavaScriptを使用します。
setTimeout関数は使えますが、UI用JavaScript用の物を利用している関係で戻り値のタイマーIDが取得できません。
タイマーIDを使うものはUI用JavaScriptを使用してください。
2つのJavaScriptエンジンで共有のオブジェクト
「g_」「Common」「Sync」「UI」「te」などの共有オブジェクトがあります。
g_ は基本的な設定など
Common はアドオン共有のオブジェクトで Common.AddressBar はアドレスバーで共有しています。
Sync は同期用JScriptの関数などを入れ、UI用JavaScriptから呼び出したりするのに使用しています。
UI は主に本体で2つのJavaScriptの連携に使用しています。(InvokeUIなど)
te は Tablacus Explorerのオブジェクトです。te.Dataに設定があります。
オブジェクト{}、配列[]の受け渡しについて
2つのJavaScriptエンジンでオブジェクト{}
、配列[]
を受け渡すと相性が悪くエラーが出やすいです。
以下のTablacus Explorerのオブジェクト、配列を使用すると問題なく動作します。
{}
の代わりに api.CreateObject("Object")
[]
の代わりに api.CreateObject("Array")
もしくはJSONなど文字列に変換して受け渡しを行ってください。
どうしてもUI用JavaScriptのオブジェクト{}
、配列[]
を同期用JScriptで読み込みたい場合は
api.ObjGetI(Object, "プロパティ名");
を使うとうまくいくかもしれません。書き込みはできません。
逆に同期用JScriptのオブジェクト{}
、配列[]
は一応読み込みできますが、未定義のプロパティを読み込むとエラーが出ます。
安全に読み書きを行う場合は
await api.ObjGetI(Object, "プロパティ名");
api.ObjPutI(Object, "プロパティ名", 新しい値);
を使用してください。
高速化テクニック
awaitが続くとどうしても遅くなるようです。
以下の固定値はui_にも置いています。
ui_ | 元 | 意味 |
---|---|---|
ui_.TEPath | await api.GetModuleFileName(null) | Tablacus Explorerのパス |
ui_.Installed | await te.Data.Installed | Tablacus Explorerのフォルダ; |
ui_.hwnd | await te.hwnd | Tablacus Explorerのウインドウハンドル |
ui_.DoubleClickTime | await sha.GetSystemInformation("DoubleClickTime") | ダブルクリックの時間; |
ui_.bit | await api.sizeof("HANDLE") * 8 | ビット数(32/64) |
配列はネイティブの配列に変換できます。
配列はawait api.CreateObject("SafeArray", 変数名);
でUI用JavaScriptのネイティブの配列に変換できます。
let ar = await api.CreateObject("Array");
await ar.push("abc", "def");
if (window.chrome) {
ar = await api.CreateObject("SafeArray", ar);
}
wsh.Popup(ar.join("\n"));
Promise.all で複数の非同期オブジェクトをまとめることもできます。
const FV = await GetFolderView();
const FolderItem = await FolderItem;
wsh.Popup([await FolderItem.Name, await FolderItem.Path].join("\n"));
同期
const FV = await GetFolderView();
const FolderItem = await FolderItem;
wsh.Popup(await Promise.all([FolderItem.Name, FolderItem.Path]).join("\n"));
非同期
const FV = await GetFolderView();
const FolderItem = await FolderItem;
Promise.all([FolderItem.Name, FolderItem.Path]).then(function (r) {
wsh.Popup(r.join("\n"));
});
非同期関数を使用しても並列動作になります。
その他注意点
UI用JavaScriptでは外部オブジェクトのlength
は取得できません。代わりにawait GetLength(
使用するか配列などの場合は上記のネイティブ配列化を使います。
const nLength = await GetLength(ar);
受け渡しの文字列にNULL文字\0
を含むとそこで切られます。
UI用JavaScriptから同期用JScriptにnull
を渡すとundefined
になります。
逆に同期用JScriptからUI用JavaScriptにundefined
を渡すとnull
になります。
以上の結果からundefined
とnull
を厳密に判別するのはお勧めしません。
if (o == null) {// null もしくは undefied の場合 true
}
== null
や!= null
を使用しています。
HTMLのエレメントは外部に持ち出すと使えなくなります。
const el = document.getElementById("○○");
const o = await api.CreateObject("Object");
o.el = el;
o.el
は別物になっています。HTMLのエレメントは必ず、UI用JavaScriptの変数に入れてください。
UI用JavaScriptのwindowオブジェクトは外部に持ち出せません
const o = await api.CreateObject("Object");
o.window = window;
上記コードはエラーが出ます。JSON化できないオブジェクトは持ち出し不可のようです。
HTML5 ドラッグ&ドロップは通常画面ではネイティブドラッグ&ドロップと共存できなかったので、ondragoverイベントとondropイベントは動作しません。ネイティブドラッグ&ドロップの方のイベントを使ってください。オプション画面の方はネイティブドラッグ&ドロップを使用していないので使えます。
※ネイティブドラッグ&ドロップのイベントはリアルタイムで戻り値を返さなければならない関係で、同期JScriptの方を使う事になるのですが、同期JScriptの方からはHTMLの部品にアクセスできないので、事前のDragEnterイベント辺りで項目の場所(RECT)を保存するなど工夫が必要で面倒だったりします。
標準版で使えるのでテキストの場合は代替でclipboardData.setData(
が使えないapi.SetClipboardData(
を使います。
Tablacus Explorer 20.12.13でWebView2版でもclipboardData.setData(
が使えるようになりました。
clipboardData.setData("text", "ABC");
api.SetClipboardData("ABC");
おまけ:標準のTrident版
標準のTrident版Tablacus Explorerの場合は一つのJavaScriptエンジンで動作し、$ = window;
になっています。
また、スクリプトの読み込み時に自動的にasync/await
などは削除していますので、Blink版と同じスクリプトで動作しています。
Promise.all
も代替処理を入れて同じコードで動作させています。
以上、試行錯誤したものです。もっと良い方法があれば教えてください。