最終更新日 2024-09-25

jsmode で .NET の COM を作る

概要

jsmodeには、「JScript」と「WebView2」の2つがあります。

- JScript WebView2
.NET 4.x
(引数や返値がプリミティブ型やプリミティブ型の配列に限定)
.NET 5.x - .NET 8.x
(プロパティもしくは引数がないメソッドしか扱えない。値はプリミティブ型やプリミティブ型の配列に限定。
(△ … hidemaruCompat.createobject など、同期タイミングのみ)


この両方で使えるCOMオブジェクトを .NET4.x で作ることが出来ます。

しかしながら、現在のところ、.NET5.x - .NET8.xでは、
「JScript」の「createobject関数」では動作しますが、
「WebView2」の「createobject関数」では正常と言える動作の確認が取れていません。
メソッドが呼び出せない。プロパティなら読み書き出来る、といった状況です。
(秀丸マクロ実行中=同期タイミング限定であれば、.NET5.x と WebView2の組み合わせでも、秀丸マクロの hidemaruCompat.createobect 経由であれば、動作します。)

jsmode用には必ず interface が必要

C#側のソース

MyTestForm.cs
using System;
using System.Runtime.InteropServices;

namespace Test
{
    public interface IFoo // 外部から利用できるメソッドに対してinterfaceの定義が必須になる。
    {
        String foo(int a);
    }

    [ComVisible(true)]
    [ClassInterface(ClassInterfaceType.None)] // これは必須
    [Guid("3BDB20EF-09BE-4B53-B662-8A9B584976E1")] // GUIDはそれぞれのclassで生成しなおすこと。
    public class Foo : IFoo // インターフェイスを継承すること
    {
        public String foo(int a)
        {
            return "Hello world!" + a.ToString() + a.ToString();
        }
    }
}

「JScriptのjsmode」と「.NET 4.x or .NET 5.x以降」の組み合わせなら、
「javascriptのオブジェクトや関数」もC#に渡せる

C#側のソース

Class1.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace ClassLibrary14
{
    public interface IClass1 // 外部から利用できるメソッドに対してinterfaceの定義が必須になる。
    {
        void CheckObj(dynamic obj);
        void CheckFunc(dynamic func);
        void CheckHGFunc(dynamic obj);
    }

    [ComVisible(true)]
    [ClassInterface(ClassInterfaceType.None)] // これは必須
    [Guid("3B0C3A24-F1C9-4ECA-BC45-0BFAF98CDC0B")]
    public class Class1 : IClass1
    {
        public void CheckObj(dynamic obj)
        {
            System.Diagnostics.Trace.WriteLine((int)obj.bbb);
        }

        public void CheckFunc(dynamic func)
        {
            int sum = func(100, 200);
            System.Diagnostics.Trace.WriteLine(sum);
        }

        public void CheckHGFunc(dynamic message_func)
        {
            message_func("OK");
        }

    }
}

秀丸マクロ側のソース

aaa.mac
js {
	var obj = createobject(currentmacrodirectory() + @"\ClassLibrary14.dll", "ClassLibrary14.Class1");
	debuginfo(2);

    message(hidemaruGlobal.message);
	var myobj = {
	   "bbb" : 3
	};

    function myfunc(a, b) { return a + b; }

	obj.CheckObj(myobj);
	obj.CheckFunc(myfunc);
	obj.CheckHGFunc(hidemaruGlobal.message);

    obj = null;

}

JavaScriptの関数を一気に登録したり、C#⇔JavaScript間を変数などもやり取りする簡易関数の作成

C#側のソース

Class1.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace ClassLibrary14
{
    public interface IClass1 // 外部から利用できるメソッドに対してinterfaceの定義が必須になる。
    {
        void RegistFunc(String key, Object obj);
        void Test();

    }

    [ComVisible(true)]
    [ClassInterface(ClassInterfaceType.None)] // これは必須
    [Guid("3B0C3A24-F1C9-4ECA-BC45-0BFAF98CDC0B")]
    public class Class1 : IClass1
    {
        Dictionary<string, object> func = new Dictionary<string, object>();
        public void RegistFunc(String key, Object obj)
        {
            func.Add(key, obj);
        }

        public void Test()
        {
            dynamic getJSVar = func["getJSVar"];
            var a = getJSVar("a");
            dynamic message = func["message"];
            message(a);
            dynamic setJSVar = func["setJSVar"];
            setJSVar("c", 500);
            setJSVar("e", "あいうえお\nかきくけこ");
        }

    }
}

秀丸マクロ側のソース

aaa.mac
js {
    var obj = createobject(currentmacrodirectory() + @"\ClassLibrary14.dll", "ClassLibrary14.Class1");
	var a = 300;

    for (var key in hidemaruGlobal) {
        if (typeof hidemaruGlobal[key] === 'function') {
           obj.RegistFunc(key, hidemaruGlobal[key]);
        } 
    }

    function getJSVar(varname) {
        return eval(varname);
    }

    function setJSVar(varname, value) {
        var expression = varname + "=" + JSON.stringify(value);
        return eval(expression);
    }

    obj.RegistFunc("getJSVar", getJSVar);
    obj.RegistFunc("setJSVar", setJSVar);

    obj.Test();

    message(c);
    message(e);
}


jsmodeがWebView2で、.NET5.0(6.0以降も同様)の場合、メソッドが呼び出せない。プロパティなら読み書き出来る

C#側のソース

Class1.cs
using System;
using System.Runtime.InteropServices;

namespace NET6COMServer
{
    [ComVisible(true)]
    [Guid("2231CB75-2BD5-40A6-907F-75F2116AF699")]
    public class NET6COMServer
    {
        public int this[int x, int y]
        {
            get
            {
                return 3;
            }
            set
            {

            }
        }
        public string str_add(string a, string b)
        {
            return a + b;
        }

        public int int_add(int a, int b)
        {
            return a + b;
        }

        private int _aaa = 3;
        public int aaa
        {
            get
            {
                return _aaa;
            }

            set
            {
                _aaa = value + 1;
            }

        }

        private int[] _bbb = [0,1,2];
        public int[] bbb
        {
            get
            {
                return _bbb;
            }

            set
            {
                _bbb = value;
            }

        }

        public string noargfunc()
        {
            return "あいうえお";
        }
    }
}

秀丸マクロ側のソース

aaa.mac
jsmode "WebView2";
js {
var com = createobject(currentmacrodirectory() + "\\NET6COMServer.comhost.dll", "{2231CB75-2BD5-40A6-907F-75F2116AF699}");
message(com);

com.aaa = 100
message(com.aaa);

let bbb = com.bbb;
message(bbb);

message(com.noargfunc);
}

JavaScriptの層でラップすればギリギリ使えなくはない感じ

C#側のソース

Class1.cs
using System;
using System.Runtime.InteropServices;

namespace NET6COMServer
{
    [ComVisible(true)]
    [Guid("2231CB75-2BD5-40A6-907F-75F2116AF699")]
    public class NET6COMServer
    {
        public object FuncArg1 = 0;

        public object FuncArg2 = "";

        public Object Func()
        {
            if (FuncArg1 is int && FuncArg2 is int)
            {
                return (int)FuncArg1 + (int)FuncArg2;
            }
            else
            {
                List<object> list = new List<object>((object[])FuncArg2);

                return FuncArg1.ToString() + list.ToArray().Length;
            }

        }
    }
}

秀丸マクロ側のソース

aaa.mac
jsmode "WebView2";
js {
var com = createobject(currentmacrodirectory() + "\\NET6COMServer.comhost.dll", "{2231CB75-2BD5-40A6-907F-75F2116AF699}");
message(com);

function Func(arg1, arg2) {
	com.FuncArg1 = arg1;
	com.FuncArg2 = arg2;

	let ret = com.Func;
    return ret;
}

let r = Func("abc",["1","2","3"]);
message(r);

}