最終更新日 2024-09-25

WPFのアプリの形態をそのまま利用する方法

概要

Hm.NetCOMをはいうなればただのインプロセス用のライブラリに過ぎませんから、
インプロセスでロード(該当秀丸プロセスと同じプロセスへとロード)されるのであれば、
.dll 形式である必要もなく、.exeでも.dll同様に扱うことが出来ます。

WPFのアプリケーションを「.NET Framework」で作成

このサンプルでは、.NET Frameworkを利用します。

Hm.NetCOM.cs を追加

プロジェクトに対して 「アセンブリを.COM参照可能とする」を付与することを忘れずに。

MainWindows.xaml を編集して、my_textbox と my_button を作りましょう。

my_button の Click のイベントハンドラからメソッドを自動生成して...

自動生成しなくとも、ご自分で MainWindow.xaml.cs を直接編集する形でももちろんOKです。

my_textbox の内容を、秀丸のアウトプット枠へと出力する短いソース例にしてみましょう。

using System;
using System.Windows;

using HmNetCOM;

namespace WpfApp1
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void On_Click(object sender, RoutedEventArgs e)
        {
            String text = this.my_textbox.Text;
            Hm.OutputPane.Output(text + "\r\n");
        }
    }
}

秀丸マクロから直接呼び出されるメソッドを定義

定義場所に迷うところですがが、 App.xaml.cs が空っぽですので、ここが良いでしょう。

using System;
using System.Windows;
using System.Runtime.InteropServices;

using HmNetCOM;

namespace WpfApp1
{
    /// <summary>
    /// App.xaml の相互作用ロジック
    /// </summary>
    [Guid("452F8C3D-A209-4DC3-ADFF-5BCABF8E4949")]
    public partial class AppHmInterface : Hm.IComDetachMethod, Hm.IComSupportX64
    {
        public static Window wpfWindow;

        public long CreateFrom()
        {
            if (wpfWindow != null)
            {
                wpfWindow.Close();
                wpfWindow = null;
            }
            wpfWindow = new MainWindow();
            wpfWindow.Show();
            return 1;
        }

        // 秀丸のバージョンで引数が渡ってくるものと渡ってこないものがあるので、
        // 両対応にするためデフォルト引数の0が必要。
        public void OnReleaseObject(int reason = 0)
        {
            // 秀丸エディタが閉じられる前に実行されるコード
            // このWPFアプリケーションを終了します。
            if (wpfWindow != null)
            {
                wpfWindow.Close();
                wpfWindow = null;

                // このメソッドが通過したことをわかりやすくするため、終了理由の番号をダイアログボックスで表示
                MessageBox.Show("あいうえお");
            }
        }

        // Hm.IComSupportX64 の実装
        public bool X64MACRO()
        {
            return true;
        }
    }
}

秀丸マクロ側のソース

呼び出し側のマクロを用意しましょう。
マクロが終わっても、WpfApp1のオブジェクトが解放されたりしないよう、keepobjectしています。
また、秀丸が終了した際に、実行してほしいメソッドとして、C#内の OnDetachMethod を指定しています。

WpfTest.mac
#obj = createobject( currentmacrodirectory + @"\WpfApp1.exe", "WpfApp1.AppHmInterface");
setcomdetachmethod #obj, "OnReleaseObject";
keepobject #obj, 1; // マクロが終了しても#objを自動でreleaseしない。

#r = member(#obj, "CreateFrom");

WpfApp1.exeと、このWpfWpfTest.mac の2つのファイルを同一のディレクトリに配置して、マクロを実行してみましょう。

On_Click は非同期メソッドなので...

非同期メソッド中は、通常マクロ中ではないため、Hm.NetCOMで利用できる機能が制限されてしまいます、
それを打開する Hm.Macro.Exec.DoMethod(...) があることを思い出してください。

MainWindowのインスタンス(this)を static メソッドから参照するため、static変数 self へと this を伝搬しているところが工夫となります。

using System;
using System.Collections.Generic;
using System.Windows;
using HmNetCOM;

namespace WpfApp1
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        static MainWindow self; // 自分自身のインスタンスをstaticメソッドから参照するためのもの。
        public static IntPtr ScopeMethod(string message_parameter)
        {
            String text = self.my_textbox.Text;
            String date = (String)Hm.Macro.Var["date"];
            Hm.OutputPane.Output($"現在の日付は{date}です\r\n");
            Hm.OutputPane.Output(text + "\r\n");
            var menu = new List<String> { "aaa", "bbb", "ccc" };
            var ret = Hm.Macro.Statement("menuarray", menu, menu.Count);
            if (ret.Error == null)
            {
                // menuarrayの選択メニューの結果は、秀丸マクロではresultに入っている。
                // 32bitでも64bitでも大丈夫なように、dynamicを挟むのを忘れないようにしよう。
                int result = (int)(dynamic)Hm.Macro.Var["result"];

                Hm.OutputPane.Output($"選択結果{result}" + "\r\n");
            }
            return (IntPtr)1;
        }

        private void On_Click(object sender, RoutedEventArgs e)
        {
            self = this; // 暗黙のインスタンス変数を直接 staticメソッドから参照できないため、this を static 変数の selfへと代入する。
            Func<String, IntPtr> method = ScopeMethod;
            Hm.Macro.Exec.Method("my_method", method);
        }
    }
}

Hm.Macro.Exec.DoMethod を利用することで、非同期メソッド中であっても、秀丸マクロを「改めて実行」するため、
Hm.Macro.Var["..."] や Hm.Macro.Funtion(...)、Hm.Macro.Statement(...) が利用可能となります。
これにより、非同期中独特の制限が大きく解けて、秀丸側から提供されているマクロ関数等の機能を利用しやすくなります。