[ 新規に投稿する ]

hm.NETを使って呼び出したWPFアプリで、ボタンイベント内のhmクラスNo.08675
kuke さん 18/04/17 22:42 [ コメントを投稿する ]
  hm.NETを使ってWPFアプリを呼び出す方法を教えてもらったので、早速、使ってみました。

すると、カーソル位置にテキストを挿入するメソッド「Hm.Edit.LineText」が、
ボタンイベント内では動作しません。
イベント内で、秀丸エディタを操作するメソッドを使用するには、
特別な記述が必要なのでしょうか?

以下のコードを実行すると、秀丸エディタに「開始」は挿入されますが、
WPFウィンドウのボタンを押しても「ボタンを押した」は、挿入されません。
ボタンのクリックイベントに一緒に記述したボタンの色を変更するコードは、
動作しているので、秀丸エディタにテキストを挿入するコードが
動作していないものと思われます。

// カーソル位置にテキストを挿入
Hm.Edit.LineText = "ボタンを押した";

以下、確認に使用したコードです。参照に、「hm.NET.dll」を追加しています。

***MainWindow.xaml***

<Window x:Class="TextInsert01.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

        Title="MainWindow" Height="150" Width="200">
    <Grid>
        <Button Name="btn0" HorizontalAlignment="Center" VerticalAlignment="Center" 
        Width="100" Height="50" Click="btn0_Click">
            押してね。
        </Button>
    </Grid>
</Window>

***MainWindow.xaml.cs***

using Hidemaru;
using System;
using System.Windows;
using System.Windows.Media;

namespace TextInsert01
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            Hm.Edit.LineText = "開始";
        }
        private void btn0_Click(object sender, RoutedEventArgs e)
        {
            // ボタンの色を変更する
            var bc = new BrushConverter();
            btn0.Background = (Brush)bc.ConvertFrom("#FFE21C1C");
            
            // カーソル位置にテキストを挿入
            Hm.Edit.LineText = "ボタンを押した";
        }
    }

    public class HmEntry
    {
        public static Window WpfForm;

        public static IntPtr CreateForm()
        {
            if (WpfForm != null)
            {
                WpfForm.Close();
                WpfForm = null;
            }
            WpfForm = new MainWindow();
            WpfForm.Show();
            return (IntPtr)1;
        }

        public static IntPtr OnDetathMethod()
        {
            if (WpfForm != null)
            {
                WpfForm.Close();
                WpfForm = null;
            }
            return (IntPtr)1;
        }
    }
}

*** 秀丸マクロ ***

#HMNET = loaddll( hidemarudir + @"\hm.NET.dll" );
 
#r = dllfuncw( #HMNET, "SetDetachMethod", currentmacrodirectory + @"\TextInsert01.exe", "TextInsert01.HmEntry", "OnDetathMethod");
#r = dllfuncw( #HMNET, "CallMethod", currentmacrodirectory + @"\TextInsert01.exe", "TextInsert01.HmEntry", "CreateForm");

[ ]
RE:08675 hm.NETを使って呼び出したWPFアプリで、ボタンイベNo.08676
vscode-life さん 18/04/18 10:39 [ コメントを投稿する ]
  まず機能しない理由ですが、「マクロ実行中ではないから」ということになります。

http://秀丸マクロ.net/?page=nobu_tool_hm_dotnet_started_01

以降の学習的な要項を一通り見て頂ければ要領が掴めるかと思います。


今回の件は
http://秀丸マクロ.net/?page=nobu_tool_hm_dotnet_started_06


Hm.Edit.LineTextの
・内容の読み取りは『常時』
・テキストの書き換えは『マクロ実行中のみ』
という項目に該当します。

マクロ実行中とは「秀丸マクロ実行中」かどうかのこととなります。
このことを確認するためには、以下のようなdebuginfoで
「MacroStart」と「MacroEnd」がどのようなタイミングで表示されるのか確認すれば、
良いこととなります。

// -------------------------------------------------------
debuginfo 1;
debuginfo "MacroStart\n";

#HMNET = loaddll( hidemarudir + @"\hm.NET.dll" );
 
#r = dllfuncw( #HMNET, .... );
#r = dllfuncw( #HMNET, .... );

debuginfo "MacroEnd\n";


以下ちょっと考え方が大きく2つに分かれますので、投稿を2つに分けます。
[ ]
RE:08676 マクロ実行中を維持するパターンNo.08678
vscode-life さん 18/04/18 10:45 [ コメントを投稿する ]
  まず、第1の方法は「マクロ実行中を維持する」ということとなります。

これの
・メリット = 記述が簡単である
・デメリット = 秀丸上ではマクロは常に1つしか実行できないのに、マクロ実行中が継続してしまうということは、他のマクロを実行することが出来ない


マクロ実行中を維持するためには次のようにShowDialogにすれば、
モーダルな形となりますので、マクロ実行中が維持出来るということとなります。

        public static IntPtr CreateForm()
        {
            if (WpfForm != null)
            {
                WpfForm.Close();
                WpfForm = null;
            }

            WpfForm = new MainWindow();
            WpfForm.ShowDialog();
            return (IntPtr)1;
        }


これでボタンを押した時のHm.Edit.LineTextも「マクロ実行中」ということとなり、機能します。
[ ]
RE:08678 文字列でマクロを実行するパターンNo.08679
vscode-life さん 18/04/18 11:20 [ コメントを投稿する ]
  次の方法は、「マクロ実行中」で縛る(ShowDialogにする)のでは「なく」、
ボタンが押された瞬間に「新たなマクロを文字列で発行する」というものです。

・メリット:1つしかないマクロを実行中にせずにすむ
・デメリット:文字列で改めてマクロを構築するのは結構難儀
       又、現在のHm.NETのバージョンだと、マルチバイトの範囲の文字でないと上手く転送できない)


        private void Btn_Click(object sender, RoutedEventArgs e)
        {

            // マクロが実行されていないなれば…
            if (!Hm.Macro.IsExecuting) {

                // マルチバイトの文字列の範囲を超えてしまうと文字化けを起こすという弱点あり
                string message = "テスト\n\tテスト\n";

                MyInsertFunc(message);

            }
        }

        private void MyInsertFunc(string message)
        {
            // 秀丸マクロの「R」を上手く使うことで、エスケープしまくり状態を回避できる
            string expression = String.Format(@"insert R""RAW({0})RAW""", message);
            // 改めてマクロを実行する
            Hm.Macro.Exec.Eval(expression);
        }
[ ]
RE:08679 ファイル名でマクロを実行するパターンNo.08680
vscode-life さん 18/04/18 11:48 [ コメントを投稿する ]
  もうひとつのパターンがファイル名でマクロを実行するパターンです。

下記では、「b.mac」というファイルを改めて用意し、
b.macからも同じWpfApp2.exeを呼び出しています。

「同じプロセス内」の「同じアセンブリ」であれば、値が継続しているという特性を利用したものとなります。

・メリット:C#コードで管理出来る
・デメリット:別途マクロが必要となっています。
 又、アセンブリの読み込まれ具合などがかなりスパゲッティとなる。

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

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

using Hidemaru;


namespace HmWPFTest
{
    public class HmWPFTestClass : System.Windows.Window
    {
        public HmWPFTestClass()
        {
            SetForm();
            SetButton();
        }

        Canvas c;
        void SetForm()
        {
            this.Title = "秀丸マクロ hmPyによるWPFに組み込むWPF";
            this.FontSize = 22;
            this.Width = 1000;
            this.Height = 500;
            c = new Canvas();
            this.Content = c;
        }

        Button btn;
        void SetButton()
        {
            var ef = new System.Windows.Media.Effects.DropShadowEffect
            {
                BlurRadius = 5,
                ShadowDepth = 1
            };

            btn = new Button()
            {
                Content = "秀丸",
                Padding = new System.Windows.Thickness(2),
                Effect = ef,
                Foreground = Brushes.White,
                Background = Brushes.Black
            };

            btn.Click += Btn_Click;
            c.Children.Add(btn);
            Canvas.SetLeft(btn, 30);
            Canvas.SetTop(btn, 100);
        }

        // ボタンが押されたら、文字列をpublicから見えるクラスに伝達して、
        // b.macを実行する。
        private void Btn_Click(object sender, RoutedEventArgs e)
        {

            // マクロが実行されていないなれば…
            if (!Hm.Macro.IsExecuting) {

                MyQueue.SetStrTrans("テスト&#9836;\n\tテスト\n");

                var thisfullpath = System.Reflection.Assembly.GetExecutingAssembly().Location;
                var dir = System.IO.Path.GetDirectoryName(thisfullpath);

                try
                {
                    var ret = Hm.Macro.Exec.File(dir + "/b.mac");
                    // マクロファイルの最後のendmacroの後に続く文字が返ってくればマクロが達成されたこととなる。
                    if (ret.Message == "MACROCOMPLETE")
                    {
                        System.Diagnostics.Trace.WriteLine("OK");
                    }
                    else
                    {
                        System.Diagnostics.Trace.WriteLine(ret.Error);
                        System.Diagnostics.Trace.WriteLine("NO");
                    }
                }
                catch (Exception err)
                {
                    System.Diagnostics.Trace.WriteLine(err);
                }

            }
        }

    }

    // b.macからみのクラス
    public class MyQueue
    {
        private static string strTrans;
        public static void SetStrTrans(string message) { strTrans = message;  }
        public static String TotalTextReplace() {
            if (strTrans != null) {
                Hm.Edit.TotalText = strTrans;
                return strTrans;
            }
            return "";
        }
    }

    public class MyEntry
    {
        public static System.Windows.Window form;

        public static IntPtr CreateForm()
        {
            if (form != null)
            {
                form.Close();
                form = null;
            }

            form = new HmWPFTestClass();
            form.Show();
            return (IntPtr)1;
        }

        public static IntPtr OnDetathMethod()
        {
            if (form != null)
            {
                form.Close();
                form = null;
            }

            return (IntPtr)1;
        }

    }
}


//------------------------- aaa.mac ---------------------------
debuginfo 1;
debuginfo "MacroStart\n";

#HMNET = loaddll( hidemarudir + @"\hm.NET.dll" );
 
#r = dllfuncw( #HMNET, "SetDetachMethod", currentmacrodirectory + @"\WpfApp2.exe", "HmWPFTest.MyEntry", "OnDetathMethod" );
#r = dllfuncw( #HMNET, "CallMethod", currentmacrodirectory + @"\WpfApp2.exe", "HmWPFTest.MyEntry", "CreateForm" );

debuginfo "MacroEnd\n";



//-------------------------- b.mac ---------------------------
#HMNET = loaddll( hidemarudir + @"\hm.NET.dll" );
 
$r = dllfuncstrw( #HMNET, "CallMethod", currentmacrodirectory + @"\WpfApp2.exe", "HmWPFTest.MyQueue", "TotalTextReplace" );

endmacro "MACROCOMPLETE";
[ ]
RE:08679 文字列でマクロを実行するパターンNo.08681
kuke さん 18/04/18 11:57 [ コメントを投稿する ]
  vscode-life さん

ありがとうございます。動作しない理由が理解できました。

秀丸マクロ.netのドキュメントは、一通り目を通したのですが、

ボタンのクリックイベントで実行するメソッドが、
「マクロ実行中ではない」こととは結びつけることができず、
説明していただいて、初めて気が付きました。

コード内のコメントの「秀丸マクロの「R」」とは、何なのでしょうか?
残念ながら、何を意図しているのかわかりません。
[ ]
RE:08681 文字列でマクロを実行するパターンNo.08682
vscode-life さん 18/04/18 12:13 [ コメントを投稿する ]
  「R」というのは秀丸マクロに比較的最近導入された機能で、
ちょうど、C++の生文字列のリテラルに近い機能です。

ちなみに秀丸マクロにはC#の@に限りなく近い「@」もあります。

秀丸に付いている「秀丸エディタマクロヘルプ」で、
検索欄に「文字列」と入れて検索してみてください。

「@」や「R」についての説明が記載されています。
[ ]
RE:08682 文字列でマクロを実行するパターンNo.08683
kuke さん 18/04/18 12:39 [ コメントを投稿する ]
  ありがとうございました「R」理解できました。
「エスケープしまくり」の「エスケープ」は、エスケープ文字のことだったのですね。
http://hidemaruo.mydns.jp:81/helpsite/hidemac/html/040_Statement_String.html
[ ]
RE:08679 文字列でマクロを実行するパターンNo.08684
vscode-life さん 18/04/19 16:21 [ コメントを投稿する ]
  これはマルチバイトでなくとも通常のC#の文字Unicodeの範囲なら問題なく実行できてる模様です。
(単純に私の表示フォントが原因でした)

[ ]

[ 新規に投稿する ]