createobject 開放時処理の提案No.10305
vscode-life さん 21/02/19 16:55
 
■createobject による#obj に setdlldetachfunc と類似の機能を設けることの提案

今、createobjectが.NET関連を見ているのですが、結構いい感じに動作している印象
を受けます。
秀丸エディタ64bit版であったとしても、(int64_t) は int32_t に対してオーバーフ
ローやアンダーフロー起こしている時には、int.Maxやint.Minに(math)clampされる
ようですね。
(秀丸にCOM関連の機能が実装された当初の経緯は知らないですが、挙動から考えてこ
れは当初からの仕様だと判断しました)

ただ、現在秀丸を閉じると、プログラム上で記載してあるデストラクタ系の処理は一
切実行されず、
「プロセスをKill」した時とほぼ類似の、「プログラム止める」&数秒後「dllアン
ロード=メモリクリア」だけされている模様であるため、
「非同期処理」を書くのはやや躊躇われます。

そこで、createobject によるオブジェクトに対しても setdlldetachfunc と趣旨が
同じ機能を実装するのが良いように思えました。
(setdlldetachfunc しない場合でもデフォルトで、DllDetachFunc_After_Hm866 に
対応するような#objに対するメソッド呼び出しがあればよいと思います)


■createobjectで、.NET Core系、.NET5系 のCOMがGUID指定しても読めない?
https://github.com/dotnet/runtime/issues/35129
にて、

Hurrah... It worked now!!!! It was both "DispId" and "CoCreateInstanceEx" th
at were needed.
Any one of the two missing, and it wouldn't work.
秀丸の内部実装がわからないため、よくわかりませんんが、あたりに関係して・・・
いますかね?

参考データ:
(上記URLのabhiphirkeさん発言中にある NotificationTest.zip
https://docs.microsoft.com/en-us/dotnet/core/native-interop/expose-components-to-com
https://docs.microsoft.com/ja-jp/dotnet/core/native-interop/expose-components-to-com
https://github.com/dotnet/samples/tree/master/core/extensions/COMServerDemo
└ InterfaceType(ComInterfaceType.InterfaceIsIUnknown)では秀丸からは見えない
ため、
  InterfaceType(ComInterfaceType.InterfaceIsBothやComInterfaceType.Interfa
ceIsDispatch あたりにするわけですが、
  やはり秀丸からは、見えないです。

.NET Core3.1 と .net5はCOMへの出し方は同じだと思います。
https://github.com/dotnet/runtime/issues/35129
のやり方が.COMとして機能することは間違いないと思います。
.NET4.x ⇔ .NET5 はアセンブリとしては非互換ですが、COM経由では上記githubの方
法で実行できています。
 https://秀丸マクロ.net/?page=nobu_tool_hm_dotnet_call_net5_from_net4

[ ]
RE:10305 createobject 開放時処理の提案No.10306
秀丸担当 さん 21/02/19 18:13
 

createobjectについては、通常であればマクロが終了した時点で自動的にreleaseobj
ect相当になり、解放されるはずになっています。
keepobjectをされているということでしょうか。
もしkeepobjectで解放しないようにしているとしたら、マクロが終了しても何もせず、
秀丸エディタのプロセスが終了するタイミングでも何もしないです。
keepobjectの場合、秀丸エディタのプロセスが終了するタイミングでreleaseobject
相当にする選択肢があったらいいと思います。

.NET Core、.NET5については、以前.NET5でGoogle検索したサンプルなどを頼りに、.
comhost.dllを試しに作ってみようとしたことがあったのですが、どうやったら作れ
るのかわからないままでした。
秀丸エディタとしては、IDispatchしか対応していないので、InterfaceIsIDispatch
(かInterfaceIsDual?)にする必要があると思うのですが、.comhost.dllがIDispat
chになるものが生成されず、.NET5の使い方がよくわからないです。
秀丸エディタはcreateobjectでprogIDを指定した場合でも、CLSIDに変換し、CoCreat
eInstanceで作成します。ここで必要なのはCLSID(GUID)のみです。
CoCreateInstanceでCLSIDを指定して作成すると、E_NOINTERFACE(0x80004002)になり
ます。マクロ上でもgetresultex(11)で値を知ることができます。
ようは.comhost.dllがIDispatchのインターフェースが無いのだと思いますが、もしI
Dispatchで作れる方法をご存知でしたら教えていただけると助かります。

ちなみに最近のバージョンのcreateobjectでDLLファイル名とCLSIDを直接指定する方
法は、CoCreateInstanceも何もWindowsのAPIは呼ばず、直接DLLの関数からIDispatch
を作成するので、ちょっと特殊で正攻法とは言えないと思うので、まずはregsvr32で
登録されていてprogIDが使えることを前提にしたほうがいいと思います。


[ ]
RE:10306 createobject 開放時処理の提案No.10307
vscode-life さん 21/02/19 18:43
 
下記のような内容は、以前、dllでデタッチファンクを制作していただいた際でも述
べたと思います
(何年も前なのでお忘れだと思います)

中間アセンブリ(CLI=.NETアセンブリ)は一度読み込むと、原則、プロセスを閉じる
まで解放されません。
(強引に解放してもバグるだけ、強引にシャットダウンさせることはできますが、同
じプロセスで正常に再ロードできなくなります))

releaseobject で解放されているのは、秀丸マクロ上の変数にすぎず、dllは解放さ
れません。

試しに以下のようなソース
- C#側 MyTestComServer3.dll としてコンパイル


using System;
using System.Runtime.InteropServices;

namespace ClassLibrary1
{
 [Guid("326E6834-E4AF-4CA3-866B-B762E42DBFBA")] //guidgenで生成する適当な別
の値にしてください
 public class Test1
 {
  static int counter = 0;
  public int GetCounter()
  {
   return counter++;
  }
 }

}

-- AssemblyInfo.cs に少なくとも以下のような2つの記述必要

// ComVisible を false に設定すると、このアセンブリ内の型は COM コンポーネン
トから
// 参照できなくなります。COM からこのアセンブリ内の型にアクセスする必要があ
る場合は、
// その型の ComVisible 属性を true に設定してください。
[assembly: ComVisible(true)]

// このプロジェクトが COM に公開される場合、次の GUID が typelib の ID にな
ります
[assembly: Guid("FCFB198A-6DF8-4611-9D76-066F08DA8CD0")]

//---------------------------------
// 秀丸マクロ側
#obj = createobject(currentmacrodirectory + @"\MyTestComServer3.dll", "Class
Library1.Test1");

#counter = member(#obj, "GetCounter");
message(str(#counter));
releaseobject(#obj);
//---------------------------------


みたいにすれば、どんどん表示される数値が0→1→2→3とマクロを実行する度に
増えていくはずです。
これは、releaseobject したとしても、秀丸マクロと秀丸は「解放したつもりになっ
ている」だけで、
実際にはdllはプロセス上で残りますし、次に再度読み込まれた際も、同じdll参照が
継続して利用されるためです。



続く

[ ]
RE:10307 createobject 開放時処理の提案No.10308
vscode-life さん 21/02/19 18:48
 
> .comhost.dll

csproj ファイルに、以下のように   <EnableComHosting>true</EnableComHosting>
を付け加えてください。
(先程投稿した秀丸マクロ.netのURLにも記載してあります)

ターゲットをx86にしてください(ANY CPUだとx64のcomhostが出来ます)

<Project Sdk="Microsoft.NET.Sdk">
 
  <PropertyGroup>
    <TargetFramework>net5.0</TargetFramework>
    <EnableComHosting>true</EnableComHosting>
    <EnableRegFreeCom>true</EnableRegFreeCom>
  </PropertyGroup>
 
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
    <PlatformTarget>x86</PlatformTarget>
  </PropertyGroup>
 
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
    <PlatformTarget>x86</PlatformTarget>
    <DebugType>none</DebugType>
    <DebugSymbols>false</DebugSymbols>
  </PropertyGroup>
 
</Project>

その後、RegAsmではなく、ネイティブと同様に、管理者権限で、RegSvr32 すればCOM
として利用可能となります。

[ ]
RE:10308 createobject 開放時処理の提案No.10309
vscode-life さん 21/02/19 22:09
 
.NET5については、IDispatch Interface付きのCOMサーバーにはお手軽な形では出来
ないかもしれません。
(ネット上の記事やMicrosoftのdotnet/githubサンプルにも無いように思えます)
(マイクロソフト的には、idl を自分で手で書いたらいけるよ、みたいななかなか無
茶な世界のようです)
現状.NET5のCOMとして提供されているものは、interfaceの型とGUIDをコントラクト
的な形で共有し、別の.NET系列(FrameworkかCoreかStandard)から呼び出すことのみ
を想定したCOMの仕組みかもしれません。

とりあえず、.NET5系以は時間がまだまだあるのでCOM関連がよくわからないのであれ
ば寝かせておくとして、
(マイクロソフトは本格的に広める統合バージョンは.NET6になってからのハズ)

releaseobject がらみの方をご検討ください。
(freedll や秀丸終了に対するsetdlldetachfuncと同様のものをreleaseobjectに対
して用意)


[ ]
RE:10309 createobject 開放時処理の提案No.10310
秀丸担当 さん 21/02/22 09:45
 

COMの作成方法について、情報ありがとうございます。
IDispatchについて、2つ理由がわかりました。
1つは、秀丸エディタのプログラムの内部的なことですが、CoCreateInstanceでIUnk
nownで作成してから、QueryInterfaceでIDispatchをしないと得られないことがわか
りました。
これはV8.96やV8.97のことではなく以前からのことですが、V8.97β3で修正してみま
す。

もう1つは、DLL側の実装方法が違うようです。
ComInterfaceType.InterfaceIsDispatchにしても、以下のようにすると、IServer(自
前のインターフェース)のGUIDでは取得できますが、IDispatchのGUIDではできないよ
うです。
    public class Server : IServer
    {
        int IServer.TestMethod(){ return 123; }
    }

以下のようにすると、IDispatchになりました。
    public class Server : IServer
    {
        public int TestMethod(){ return 123; }
    }

これだとjsやvbsからも使えるのでよさげのようです。
秀丸エディタではregsvr32せずに、createobject($file,$clsid)の方法でもできまし
た。


プロセス終了時の.netのDLLの解放については、前にも話題があったようで失礼しま
した。
何かCOMの仕組みとして標準でデストラクタ的なものがあったらよさそうな気がして、
調べてみたところIDisposableというのがありそうだったのですが、呼ぶ側も呼ばれ
る側も、どの環境で作成したものでも通用するのか不安そうでした。(IDispatchで
すらあやふやなので)
普通にメソッド呼び出しのようが安定するかもしれません。
V8.97は近いうちに正式にしたいと考えていますが、setdlldetachfunc相当の検討は
将来的なこととして、とりあえずIDispatchの対策だけ先にやろうと思います。


[ ]
RE:10310 createobject 開放時処理の提案No.10312
vscode-life さん 21/02/22 13:39
 

#obj = createobject( "\****.comhost.dllへのパス", "{clsid}" ); の形で読める
ことを確認しました。


★「net core3.1」「net5」「net6」でこの形で呼べることを確認しました。。
(net6は現在preview.1ですが、ここの有り様が変わる予定はnet6の段階ではなかろう
と思います)

@net framework 4.x と net core系(core, 5, 6) でCOMの作り方が違うこと、
A検索するとおそらくほとんどの人がここにひっかかると思われる
 マイクロソフトが示す.NET Core系COM公開の際のクラスの作り方だと、秀丸では読
めないこと、
B同じ.netだが、net 4.xはProgIdを示して読めること、
C(core, 5, 6)は今のところ、ClsIdを示す必要があること。

などなどから違いを強調し、.net framework とともに .net core(net 5)のサンプル
も明示しないと、
罠が多すぎて混乱不可避だと思いました。


なんといっても8.96からレジストリ登録不要になっていることでディレクトリ引っ越
しや他者利用の自由度が上がったので、すごく便利だと思います。


[ ]
RE:10312 createobject 開放時処理の提案No.10313
vscode-life さん 21/02/22 13:42
 
↑8.97 β3での確認です。

[ ]
RE:10312 createobject 開放時処理の提案No.10314
vscode-life さん 21/02/22 14:19
 
>@net framework 4.x と net core系(core, 5, 6) でCOMの作り方が違うこと、
>A検索するとおそらくほとんどの人がここにひっかかると思われる
> マイクロソフトが示す.NET Core系COM公開の際のクラスの作り方だと、秀丸では
>読めないこと、
>B同じ.netだが、net 4.xはProgIdを示して読めること、
>C(core, 5, 6)は今のところ、ClsIdを示す必要があること。

Dプロジェクトのビルド対象プラットフォームをx86にする必要があること
 ANY CPUだと しれっと x64 の ***.comhost.dll が出るので、いわゆるノーマルの
秀丸(32bit edition)からは読めない

これも多分、4.xだとANY CPU で、32bit/64bit両方動作していたので、罠になりやす
いです。
(特にC#でプログラム書く人は、3rdのネイティブdllを読んでもいないプロジェクト
でANY CPUをx86かx64どちらか固定にすることはまずしないため)

[ ]
RE:10314 createobject 開放時処理の提案No.10316
秀丸担当 さん 21/02/22 15:06
 

早速のご確認ありがとうございます。
ヘルプにいろいろ注意書きがあったほうがいいと思います。
追記しておこうと思います。

[ ]