入門編 ~より優れた監視とTick~

概要

これまでのサンプルコードでは、解説を単純にするため、.NET FrameworkのFormクラスに備わった、
Tickの仕組みに乗っかる方法を採用していました。
これは単純に制作できる反面、このTickは本体の秀丸と同じ描画スレッドに乗っかる形となるため、
重い処理をすると、秀丸の操作にひっかかりが出るようになってしまいます。

よって秀丸本体での操作を妨げないdllの制作方針としては、
以下のように、「新たなタスク」を作り、重い監視処理はそちらのスレッドで行うような形とするのがベスト
でしょう。

Task と CancellationTokenSource を使った監視

  • C#側のソース

    HmUpcateChecker.dllとしてコンパイル
    using System;
    using System.Threading;
    using System.Threading.Tasks;
    
    using Hidemaru;
    
    
    public class UpdateChecker
    {
    
        Task task;
        CancellationTokenSource tokenSource;
    
        string preLastString = "";
    
    
        public UpdateChecker()
        {
            preLastString = Hm.Edit.TotalText ?? "";
            CreateTimer();
        }
    
        ~UpdateChecker()
        {
            StopTimer();
        }
    
        // 改めて「秀丸マクロ空間」として実行可能なメソッド
        // Hm.***.*** な各種メソッドが利用可能
        public static IntPtr TickMethod(string message_parameter)
        {
            Hm.OutputPane.Output("変化検知\r\n");
    
            return (IntPtr)1;
        }
    
        void HeavyMethod(CancellationToken token)
        {
            while (true)
            {
                // キャンセルがリクエストされた
                if (token.IsCancellationRequested) { break; }
    
                bool is_must_update = false;
    
                string selectedText = Hm.Edit.TotalText ?? "";
    
                // テキストが変更された
                if (selectedText != preLastString)
                {
                    is_must_update = true;
                    preLastString = selectedText;
                }
    
                // 更新するべきだ かつ マクロは実行中ではない
                if (is_must_update && !Hm.Macro.IsExecuting)
                {
                    Func<String, IntPtr> method = TickMethod;
                    var ret =  Hm.Macro.Exec.Method("EndMacro", method);
                    // 最後まで実行できた?
                    if (ret.Message == "EndMacro")
                    {
                        ;
                    }
                    else
                    {
                        System.Diagnostics.Trace.WriteLine("マクロ中にエラーが発生している可能性があります。");
                        this.StopTimer();
                    }
                }
    
                Thread.Sleep(1000);
            }
        }
    
        public void CreateTimer()
        {
            tokenSource = new CancellationTokenSource();
            var token = tokenSource.Token;
    
            task = Task.Run(() => HeavyMethod(token));
    
        }
    
        public void StopTimer()
        {
            if (tokenSource != null)
            {
                tokenSource.Cancel();
            }
        }
    }
    
    public class HmUpdateChecker
    {
        static UpdateChecker uc;
        public static IntPtr StartChecker()
        {
            StopChecker();
            if (uc == null)
            {
                uc = new UpdateChecker();
            }
    
            return (IntPtr)1;
        }
    
        public static IntPtr IsCheckMode()
        {
            if (uc != null)
            {
                return (IntPtr)1;
            }
            else
            {
                return (IntPtr)0;
            }
        }
    
        public static IntPtr StopChecker()
        {
            if (uc != null)
            {
                uc.StopTimer();
                uc = null;
            }
    
            return (IntPtr)1;
        }
    
    
        public static IntPtr OnDetachMethod(IntPtr reason)
        {
            IntPtr ret = StopChecker();
            return ret;
        }
    }
    
  • 秀丸マクロ側のソース

    HmUpcateChecker.mac
    #HMNET = loaddll( hidemarudir + @"\hm.NET.dll" );
     
    if ( !#HMNET ) { message "hm.NET.dllが読み込めない"; }
    
    #result = dllfuncw( #HMNET, "SetDetachMethod", currentmacrodirectory + @"\HmUpdateChecker.dll", "HmUpdateChecker", "OnDetachMethod");
    #result = dllfuncw( #HMNET, "CallMethod", currentmacrodirectory + @"\HmUpdateChecker.dll", "HmUpdateChecker", "StartChecker");