getFunctionId の 挙動がオカシイ(多分変No.11656
こみやんま さん 24/05/25 15:12
 
マクロライブラリにアップした
HmSplitTextFileByLine, HmSplitTextFileBySize,
HmSplitTextFileByRegex(←おそらく月曜か火曜に表示されるんであろう)

で制作している際に気づいたのですが、
「レンダリング枠」と「getFunctionId」を初めてまともに使用してみたのですが、
恐らく getFunctionId はバグっています。
(避けられないバグというわけではないです)

おそらく 「jsmodeの登場時」に「既存のものと同じjsmode空間名」が登場すると、
その空間名にぶら下がった funcidがクリアされ、実態が見えなくなるんだろうと思
います。


■再現ソース

---- test.mac------

hidemaruversion "9.25.99";


jsmode "WebView2";

js {
debuginfo(2);
console.log(hidemaru.getJsMode());

if (typeof (idInterval_MyUITemplate) != "undefined") {
    console.log("クリア");
    hidemaru.clearInterval(idInterval_MyUITemplate);
}

class MyUITemplate { // ここのクラス名はマクロファイル名ごとに書き換える

    constructor() {
        debuginfo(2);

        MyUITemplate.strTargetLabel = "RenderInputHtml";
        MyUITemplate.openRenderPane();
    }

    static outputAlert(err) {
        let dll = loaddll("HmOutputPane.dll");
        dll.dllFunc.Output(hidemaru.getCurrentWindowHandle(), err + "\r\n");
    }

    static openRenderPane() {
        let absoluteUrl = new URL(currentmacrodirectory() + "\\" + "RenderIn
put.html");
        let idCallBack = hidemaru.getFunctionId(MyUITemplate.onHtmlButtonCli
ck);
        console.log(idCallBack + "\r\n");
        let params = {
          strIDCallBack: idCallBack,
        };
        absoluteUrl.search = new URLSearchParams(params).toString();

        console.log(absoluteUrl.href);

        const json_arg = {
            target: MyUITemplate.strTargetLabel,
            uri: absoluteUrl,
            show: 1,
            place: "leftside",
        };
       
        renderpanecommand(json_arg);
    }

    static checkComplete() {
        console.log("checkComplete");
        try {
        let readyState = renderpanecommand({ target: "RenderInputHtml", get:
 "readyState" });
        if (readyState == "complete") {
            hidemaru.clearInterval(idInterval_MyUITemplate);
            console.log("complete");
            MyUITemplate.onRenderPaneShown();
        }
        } catch(err) {
            MyUITemplate.outputAlert(err);
        }
    }

    static onRenderPaneShown() {
        console.log("onRenderPaneShown");
        try {
/*
        renderpanecommand({
            target: "RenderInputHtml",
            focus: 1,
        });
*/
        } catch(err) {
            MyUITemplate.outputAlert(err);
        }
    }

 static onHtmlButtonClick(json) {
     try {
         console.log("OK3");
         console.log(idInterval_MyUITemplate);
         hidemaru.clearInterval(idInterval_MyUITemplate);
         renderpanecommand({
             target: "RenderInputHtml",
             show: 0,
         });
         console.log("OK4");
         console.log("OK5");
         let strInputJson = json;
         console.log(json);
         hidemaru.postExecMacroMemory( "js {onPostExecute()}" );
     } catch(err) {
         MyUITemplate.outputAlert(err);
     }
 }



}



try {
    idInterval_MyUITemplate = hidemaru.setInterval(MyUITemplate.checkComplet
e, 300);
    var myUITemplate = new MyUITemplate(); // let ではなく寿命が残るvarであ
る必要がある。
} catch(err) {
}

} // js


jsmode "WebView2";

js {
    debuginfo(2);
    console.log(hidemaru.getJsMode());

    function onPostExecute() {
         console.log("OK6");
    }
}

//-----------------------------------------


■再現リソース

---- RenderInput.html------

<!DOCTYPE html>
<html>

<head>
    <title>ボタンクリックサンプル</title>
</head>

<body>
    1番目のパラメータ:<br>
    &nbsp;<input type="text" id="input_1"><br>
    <br>
    2番目のパラメータ:<br>
    &nbsp;<input type="text" id="input_2"><br>
    <br>
    &nbsp;<button id="button_1">OK</button><br>

    <script>
        // ボタンを取得
        const btn_1 = document.getElementById("button_1");
        // window.alert(btn_1);

        let idCallback = 0;
     // 現在のURLを取得
     var url = new URL(window.location.href);
       // パラメータを取得
        var params = new URLSearchParams(url.search);
     let strIDCallBack = params.get('strIDCallBack');
     idCallback = Number(strIDCallBack);

        // window.alert(idCallback);
        try {
            // window.alert("OK");
            // ボタンがクリックされた時の処理
            btn_1.addEventListener("click", function () {
                window.alert(idCallback);
                let message_obj = {
                    input_1: input_1.value,
                    input_2: input_2.value,
                };
                let json = JSON.stringify(message_obj);
                window.alert(json);
                window.chrome.webview.postMessage({ funcid: idCallback, mess
age: json });
            });
        }
        catch (err) {
            // window.alert(err);
        }
    </script>
</body>

</html>

//-----------------------------------------


test.mac 実行し、レンダリングパネルのボタンを押しても、

onHtmlButtonClick は実行されないことがわかります。
(HTMLのjavascriptまでは実行されるが、funcidなどが渡っていても、postMessageを
受け付けたあと、肝心のwebview2内の関数が実行されない)


一度、秀丸上の全てのファイルを閉じて、 改めて、 test.macを開き、
以下のように編集します。

test.macの一番下の部分

// ------------------------------

/* 2回目のjsmodeをコメントアウトする
jsmode "WebView2";

js {
    debuginfo(2);
    console.log(hidemaru.getJsMode());

    function onPostExecute() {
         console.log("OK6");
    }
}
*/

// ------------------------------------


なんとなんど、今度はちゃんと 実行されます。(-o-;) !!!!


以上のことにより、同じ「jsmode空間」の宣言のタイミングで
getFunctionId が正しくないのだと思います。
 (実際には getFunctionId そのものがバグってるのではなく、
この関数が返す番号の元となる登録管理情報が jsmode 登場時に何か変わってる)

[ ]
RE:11656 getFunctionId の 挙動がオカシNo.11657
こみやんま さん 24/05/25 15:15
 
> なんとなんど、今度はちゃんと 実行されます。(-o-;) !!!!

もちろん、コメントアウトしたので、onPostExecute が見つからないというエラーは
出ます。
(が、ちゃんと getFunctionIdとpostMessage は機能しているという証拠)

[ ]
RE:11657 getFunctionId の 挙動がオカシNo.11658
こみやんま さん 24/05/25 15:25
 
というか、jsmode ではなく、「2個目の js { }」が 出てきただけでダメっぽいかもw

[ ]
RE:11658 getFunctionId の 挙動がオカシNo.11660
秀丸担当 さん 24/05/27 12:29
 
バグ情報ありがとうございます。
確かに言われているような挙動になっているように見えます。
もう少し調べてみます。

[ ]
RE:11660 getFunctionId の 挙動がオカシNo.11661
こみやんま さん 24/05/27 14:59
 
なんかすごい不安定というか、あちこち変えても動作するようになりますね〜

原因がもしかすると1つではなく2つある?


以下は短くしてみましたが、やはり動作しません。
(動作するマシンがある可能性がある気がしてきました。)



hidemaruversion "9.25.99";


jsmode "WebView2";

js {
    debuginfo(2);
    if (typeof (idInterval_MyUITemplate) != "undefined") {
        console.log("クリア");
        hidemaru.clearInterval(idInterval_MyUITemplate);
    }

    class MyUITemplate { // ここのクラス名はマクロファイル名ごとに書き換える

        constructor() {
            MyUITemplate.openRenderPane();
        }

        static outputAlert(err) {
            let dll = loaddll("HmOutputPane.dll");
            dll.dllFunc.Output(hidemaru.getCurrentWindowHandle(), err + "\r\
n");
        }

        static openRenderPane() {
            let idCallBack = hidemaru.getFunctionId(MyUITemplate.onHtmlButto
nClick);
            let absoluteUrl = new URL(currentmacrodirectory() + "\\" + "Rend
erInput.html").href;
            absoluteUrl = absoluteUrl + "?strIDCallBack=" + idCallBack;
            const json_arg = {
                target: "RenderInputHtml",
                uri: absoluteUrl,
                show: 1,
            };

            renderpanecommand(json_arg);
            console.log("URLをrendercommand");
        }

        static checkComplete() {
            try {
                let readyState = renderpanecommand({ target: "RenderInputHtm
l", get: "readyState" });
                if (readyState == "complete") {
                    hidemaru.clearInterval(idInterval_MyUITemplate);
                    console.log("URLをcomplete");
                } else {
                    console.log("URLはimcomplete");
                }
            } catch (err) {
                MyUITemplate.outputAlert(err);
            }
        }

        static onHtmlButtonClick(json) {
            console.log("onHtmlButtonClick\r\n");
        }
    }



    try {
        idInterval_MyUITemplate = hidemaru.setInterval(MyUITemplate.checkCom
plete, 300);
        var myUITemplate = new MyUITemplate(); // let ではなく寿命が残るvar
である必要がある。
    } catch (err) {
        console.log(err);
    }

} // js


jsmode "WebView2";

js {
    function onPostExecute2() {
        console.log("OK6");
    }
}


しかし、行を入れ替えるだけで動作するようになります。

        idInterval_MyUITemplate = hidemaru.setInterval(MyUITemplate.checkCom
plete, 300);
        var myUITemplate = new MyUITemplate(); // let ではなく寿命が残るvar
である必要がある。


の2行を


        var myUITemplate = new MyUITemplate(); // let ではなく寿命が残るvar
である必要がある。
        idInterval_MyUITemplate = hidemaru.setInterval(MyUITemplate.checkCom
plete, 300);

と入れ替えるだけでまず間違いなく動作するようになります。
(2個目のjsmodeはあろうがなかろうが動作に響かないようになる)


又、行を入れ替えずとも
300 ではなく、2000とかにするだけでも問題がなくなります。


readyState = renderpanecommand({ target: "RenderInputHtml", get: "readyState
" })

の問い合わせが、

「レンダリングパネルのcomplete後であったとしても、何かのステータスより前に r
enderpanecommand({ target: "RenderInputHtml", get: "readyState" });

みたいな問い合わせがあると、funcidの情報と紐づけたpostMessageが機能しなくな
る??


[ ]
RE:11661 getFunctionId の 挙動がオカシNo.11662
秀丸担当 さん 24/05/27 17:12
 
自分も調べてみたのですが、なにやらsetInterval、clearInterval周りと、WebView2
が合わさるとなぜかおかしくなるようです。
JScript化したものだとどの場合も問題ないです。
setIntervalあたりを何もかも自前にしたら、いまのところうまくいっていそうです。
再現せず不安定なら厄介でしたが、一定の再現パターンがあるので、なんとか修正し
ます。

[ ]
RE:11662 getFunctionId の 挙動がオカシNo.11663
western さん 24/05/28 00:30
 
レンダリング枠がらみということで便乗で検証してみましたが、
「setInterval、clearInterval周りと WebView2」まで絞り込み出来ているのと同じ
切り分け確認できました。
以下、最小コードです

jsmode "WebView2";
js {
 debuginfo(2);

 // hidemaru. 付かない JavaScript 標準の setInterval であればトラブル起きない
 // setTimeout / clearTimeout については hidemaru. 付きでも無しでもトラブル
起きない

 // renderpanecommand より先に hidemaru.setInterval() を設定した場合にのみ起
きる
 var intervalId = hidemaru.setInterval(interval, 300); // 330〜500より大きけ
れば問題なし

 // ここで hidemaru.clearInterval(intervalId) する分にはトラブル起きない

 function interval() {
  console.log("fire clearTimeout");

  // renderpanecommand() がコールされてから一定時間(300〜400ms)以内に
  // hidemaru.setInterval() の返り値を (※他の値なら問題なし)
  // ハンドラの中で hidemaru.clearInterval(intervalId) するとコールバックが
効かなくなる
  hidemaru.clearInterval(intervalId);
 }

 const json_arg = {
  target: "RenderInputHtml",
  uri: "file:///" + currentmacrodirectory() + "\\RenderInput.html?strIDCallB
ack=" + hidemaru.getFunctionId(callback),
  show: 1,
  place: "leftside",
 };
 renderpanecommand(json_arg); // renderpanecommand より先に hidemaru.setInte
rval() を設定した場合にのみ起きる
 function callback() {
  console.log("fire callback!");
 }
}

[ ]
RE:11663 getFunctionId の 挙動がオカシNo.11664
western さん 24/05/28 07:31
 
hidemaru.clearInterval のハンドラ内の処理に限らないのでは? と
並行処理においてありえそうな状況として、以下のロジックでも再現確認しました

// コールバックに使いたい関数ポインタを登録 json_arg(略)
var callbackId = hidemaru.getFunctionId(callback);

// ここでセットした hidemaru. なインターバルタイマ(の関数ポインタ)が
var intervalId = hidemaru.setInterval(() => { }, 200);

// 実行コストが大きい renderpanecommand の実行中にタイムアウトした場合に
renderpanecommand(json_arg);

// その直後に hidemaru. なインターバルタイマを解除することによって
hidemaru.clearInterval(intervalId);

// callbackId から参照してる関数ポインタを巻き込んでいるのではないか

[ ]
RE:11664 getFunctionId の 挙動がオカシNo.11665
こみやんま さん 24/05/28 07:43
 
>// 実行コストが大きい renderpanecommand の実行中にタイムアウトした場合に
>renderpanecommand(json_arg);


普通に実行すると関数呼び出しから返ってくるのが遅い json_arg に

const json_arg = {
    target: "RenderInputHtml",
    uri: absoluteUrl,
    show: 1,
    initialize: "async",
};

renderpanecommand(json_arg);

と initialize: "async" を入れ込むと、恐らく大丈夫なので、
なにか、この一種「エンジン側とブラウザ側とのWebViewとのロック」的なことが起
きてる間に事件が発生している気がしますねぇ。

[ ]
RE:11665 getFunctionId の 挙動がオカシNo.11666
秀丸担当 さん 24/05/28 12:29
 
westernさん、こみやんまさん、大変詳しい情報ありがとうございます。
確かにsetIntervalしてからのrenderpanecommandの時間のかかる処理中のことが条件
となっていました。
そのあたりも含めて修正させていただきます。

[ ]