[ 新規に投稿する ]

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() + "\\" + "RenderInput.html");
        let idCallBack = hidemaru.getFunctionId(MyUITemplate.onHtmlButtonClick);
        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.checkComplete, 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, message: 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.onHtmlButtonClick);
            let absoluteUrl = new URL(currentmacrodirectory() + "\\" + "RenderInput.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: "RenderInputHtml", 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.checkComplete, 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.checkComplete, 300);
        var myUITemplate = new MyUITemplate(); // let ではなく寿命が残るvarである必要がある。


の2行を


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

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


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


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

の問い合わせが、

「レンダリングパネルのcomplete後であったとしても、何かのステータスより前に renderpanecommand({ 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?strIDCallBack=" + hidemaru.getFunctionId(callback),
  show: 1,
  place: "leftside",
 };
 renderpanecommand(json_arg); // renderpanecommand より先に hidemaru.setInterval() を設定した場合にのみ起きる
 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の時間のかかる処理中のことが条件となっていました。
そのあたりも含めて修正させていただきます。
[ ]

[ 新規に投稿する ]