【VBA】 vb2clr を使用して .NET Framework 4の機能を利用する

# はじめに
以前、`VBScript.dll`が2027年以降に廃止となることから `Microsoft VBScript Regular Expressions 5.5`を参照した正規表現(RegExp)の代替案を書きました。
[【VBA】vba-regexを使用した正規表現(VBScript廃止の代替)](https://qiita.com/yaju/items/23f659b9e108cda5e172)

[Selenium VBA の discussions 158](https://github.com/GCuser99/SeleniumVBA/discussions/158) の中で、別案として .NET Framework の正規表現クラスを利用した方法を提示してくれた方がいます。

https://github.com/6DiegoDiego9/VBA-dotNET-regex

この技術の元になる技術が、`vb2clr` ライブラリで日本の方が開発しています。

:::note info
VBA から .NET Frameworkの機能を使用する方法は以前にありましたが、その場合、別途 .NET Framework 3.5のインストールが必要でした。
**これは、別の方法を使用しているため、.NET Framework 3.5のインストールは不要となります。**
:::

# vb2clr の紹介
筆者も以前に記事を読んだことがあったのですが、何か難しいイメージがあってスルーしていたのですが、改めて見直しました。

* https://github.com/jet2jet/vb2clr
* [VBからCLR(.NET)を利用する その1[準備編]](https://www.pg-fl.jp/program/tips/vb2clr1.htm)
* [VBからCLR(.NET)を利用する その2[呼び出し編]](https://www.pg-fl.jp/program/tips/vb2clr2.htm)
* [VBからCLR(.NET)を利用する その3[発展編]](https://www.pg-fl.jp/program/tips/vb2clr3.htm)

vb2clrのライセンスは「修正BSDライセンス」(3条項BSDライセンス; BSD-3-Clause)となっています。

# 使い方
## 導入方法
1. https://github.com/jet2jet/vb2clr からモジュールを取得します。
2. 次に`ExitHandler.bas`と`CLRHost.cls`ファイルをプロジェクトにドラッグ＆ドロップします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/392991e3-4f27-4dc8-977e-d2c482cd8ec3.png)

### 補足
vb2clrは日本人の方が開発しており、Excel関連ファイルはShift-JISを用いている。
そのため、海外向けとしてUTF-8用に変換されたファイルが用意されている。
  * ExitHandler.utf8.bas
  * CLRHost.utf8.cls

## 参照設定
ツールの参照設定から次の2つのライブラリを参照します。
* Common Runtime Language Execution Engine
* mscorlib.dll

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/ef7ceda6-b780-497a-8b1d-d1b6692dea27.png)

:::note info
どちらも Windows 10 以降の環境であれば標準で搭載されているため、Excelマクロファイルの配布に支障はありません。
:::

# プログラム
正規表現は、GitHub上に掲載されているので、それ以外のサンプルプログラムを用意しました。
最初は理解できないで途方に暮れていたのですが、ネット検索しまくったところ中国の掲示板にサンプルプログラムが投稿されており突破口が開かれました。

[[分享] vba里使用.net类](https://club.excelhome.net/thread-1692104-1-1.html)

その中の一部を日本語に書き換えています。
[vb2clr 使い方](https://yaju.sakura.ne.jp/wiki/doku.php?id=it%E6%8A%80%E8%A1%93:vb2clr#%E4%BD%BF%E3%81%84%E6%96%B9)

## StringBuilderのテスト

```vb
Sub StringbuildTest()
    Dim host As New CLRHost
 
    Call host.Initialize(False)
 
    Dim Assem As mscorlib.Assembly, sb As mscorlib.Object, r As Variant
    host.CLRLoadAssembly ("System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")
 
    Set sb = host.ToObject(host.CLRCreateObject("System.Text.StringBuilder"))
    Set sb = host.CLRInvokeMethod(sb, "Append", "The quick brown fox jumped over the lazy dog. ")
    Set sb = host.CLRInvokeMethod(sb, "Append", "Mary had a little lamb.")
 
    Debug.Print host.CLRProperty(sb, "Length")
    r = host.CLRInvokeMethod(sb, "ToString")
    Debug.Print r
 
    Set sb = Nothing
    Set host = Nothing
End Sub
```

## SortedListとArrayListのテスト
```vb
Sub SortedlistTest()
    Dim host As New CLRHost
 
    Call host.Initialize(False)
 
    Dim Assem As mscorlib.Assembly, mySL As Object, Total%, i%, aList As Object, interfacelist As Object
    host.CLRLoadAssembly ("System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")
    Set Assem = host.CLRLoadAssembly("System.Collections, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")
    Set mySL = host.ToObject(host.CLRCreateObject("System.Collections.SortedList"))
 
    mySL.Add "Third", "!"
    mySL.Add "Second", "World"
    mySL.Add "First", "Hello"
 
    Total = mySL.Count
    Debug.Print "番号：", Total
 
    For i = 0 To Total - 1
        Debug.Print mySL.GetKey(i), mySL.GetByIndex(i)
    Next
 
    Set mySL = Nothing
    Stop
 
    Set aList = host.ToObject(host.CLRCreateObject("System.Collections.ArrayList"))
    aList.Add ("Hello")
    aList.Add ("World")
    aList.Add ("!")
 
    For Each key In aList
        Debug.Print key
    Next key
 
    Debug.Print aList.IndexOf("!", 0)
    Set aList = Nothing
    Set host = Nothing
End Sub
```

## SHAハッシュの取得
これは筆者の独自のものになります。

Excelを便利にする250以上の機能を体系化したアドインに「RelaxTools Addin for Excel」があります。
* [Windows10にしたらVBAから.Net Frameworkが使えなくなった](https://qiita.com/RelaxTools/items/ba06a14fe6865a418b06)
* [Windows 11 対応](https://software.opensquare.net/relaxtools/archives/3892/)

この中で「ハッシュ関数のユーザ定義関数を使用するためには、.Net 3.5をインストールする必要があります。」との記載があります。
これが解決できると思い組んでみました。

```vb
Private Sub ComputeSHATest()
 
    Debug.Print "SHA256"
    Debug.Print GetSHA256(Range("A1"))
 
    Debug.Print "SHA384"
    Debug.Print GetSHA384(Range("A1"))
 
    Debug.Print "SHA512"
    Debug.Print GetSHA512(Range("A1"))
End Sub
```

A1セルに"Hello World"をセット、C#で求めたハッシュ値と同じになりました。

```text:結果
SHA256
a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e
SHA384
99514329186b2f6ae4a1329e7ee6c610a729636335174ac6b740f9028396fcc803d0e93863a7c3d90f86beee782f4f3f
SHA512
2c74fd17edafd80e8447b0d46741ee243b7eb74dd2149a0ab1b9246fb30382f27e853d8585719e0e67cbda0daa8f51671064615d645ae27acb15bfb1447f459b
```

```vb:basFunction
Option Explicit
Private Const C_SHA256 As Long = 1
Private Const C_SHA384 As Long = 2
Private Const C_SHA512 As Long = 3
'--------------------------------------------------------------
'　SHA256算出関数
'--------------------------------------------------------------
Public Function GetSHA256(ハッシュ値算出範囲 As Range) As Variant
    Application.Volatile
    GetSHA256 = ComputeSHA(C_SHA256, ハッシュ値算出範囲)
 
End Function
'--------------------------------------------------------------
'　SHA384算出関数
'--------------------------------------------------------------
Public Function GetSHA384(ハッシュ値算出範囲 As Range) As Variant
    Application.Volatile
    GetSHA384 = ComputeSHA(C_SHA384, ハッシュ値算出範囲)
 
End Function
'--------------------------------------------------------------
'　SHA2512算出関数
'--------------------------------------------------------------
Public Function GetSHA512(ハッシュ値算出範囲 As Range) As Variant
    Application.Volatile
    GetSHA512 = ComputeSHA(C_SHA512, ハッシュ値算出範囲)
 
End Function
 
Private Function ComputeSHA(lngType As Long, r As Range) As Variant
 
    Dim host As New CLRHost
    Call host.Initialize(False)
    host.CLRLoadAssembly ("System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")
 
    Dim str As String
    'Rangeの文字列結合（ワークシート関数のConcatを流用）
    str = Application.WorksheetFunction.Concat(r)
 
    'バイト読み込み
    Dim code() As Byte
    Dim objUTF8 As mscorlib.Object
    Set objUTF8 = host.ToObject(host.CLRCreateObject("System.Text.UTF8Encoding"))
    code = host.CLRInvokeMethod(objUTF8, "GetBytes", str)
    Set objUTF8 = Nothing
 
    'ハッシュ値計算
    Dim hashValue() As Byte
    Dim objSHA As mscorlib.Object
    Select Case lngType
        Case C_SHA256
            Set objSHA = host.ToObject(host.CLRCreateObject("System.Security.Cryptography.SHA256Managed"))
        Case C_SHA384
            Set objSHA = host.ToObject(host.CLRCreateObject("System.Security.Cryptography.SHA384Managed"))
        Case C_SHA512
            Set objSHA = host.ToObject(host.CLRCreateObject("System.Security.Cryptography.SHA512Managed"))
    End Select
    hashValue = host.CLRInvokeMethod(objSHA, "ComputeHash", code)
    Set objSHA = Nothing
 
    '16進数へ変換
    Dim description As String
 
    description = ""
 
    Dim i As Long
    For i = LBound(hashValue()) To UBound(hashValue())
        description = description & Right("0" & Hex(hashValue(i)), 2)
    Next i
 
    'return
    ComputeSHA = LCase(description)
 
    Set host = Nothing
End Function
```

## 注意点
[補足: サポートクラス「CLRHost」](https://www.pg-fl.jp/program/tips/vb2clr3.htm)

CLRHostクラスのインスタンスを利用し終わった場合は、必ずインスタンスを解放する

# 技術的な補足
## COMベースではない
VB や VBA は COMベースでプログラムが実行されるため、COM相互運用をサポートした.NETアセンブリであれば直接利用することができますが、`System.Xml`などサポートしていないアセンブリは利用することができませんでした。
また、.NET Framework 4.5 からは、.NET型をCOMインターフェイスとして公開することを停止してしまいました。このため、.Net Framework 3.5をインストールする必要がありました。

:::note info
vb2clrライブラリでは、COMベースではないため、ほぼすべての.NETアセンブリを利用可能になります。
:::

## mscorlib とは
`mscorlib`というのは何なのかというと、.NET Framework の標準ライブラリのコードを含むアセンブリ（DLL）のうち最も重要なものになります。
`System.Object`型や`System.String`型などの定義はすべてこの中にあります。
ちなみに「Multilanguage Standard Common Object Runtime Library」の略です。

`mscorlib.dll`には`System.String`の他に、.NETフレームワークの基本となる多くのクラスや機能が定義されています。

## 公開キートークン
.NET Frameworkライブラリーを使用するのに公開キートークンを知る必要がある。

```vb:例
host.CLRLoadAssembly ("System.IO, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")
```

### 調べ方
VS 2022用開発者コマンドプロンプトを起動します。
ネイティブx64バージョン（[ x64 Native Tools Command Prompt for VS 2022 ] ）を選択してください。

例として、「System.dll」を調べます。

```
cd "C:\Windows\Microsoft.NET\Framework64\v4.0.30319"

C:\Windows\Microsoft.NET\Framework64\v4.0.30319>SN.exe -T "System.dll"

Microsoft(R) .NET Framework Strong Name Utility バージョン 4.0.30319.0
Copyright (c) Microsoft Corporation. All rights reserved.

公開キー トークン b77a5c561934e089
```

### よく使用するもの
アセンブリにより同じ公開トークン(PublicKeyToken)を使用している。

|モジュール|公開トークン|
|:--|:--|
|System|System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089|
|System.IO.Compression.FileSystem|System.IO.Compression.FileSystem, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089||System.Collections|System.Collections, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a|
|System.IO|System.IO, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a|
|System.Text.Encoding|System.Text.Encoding, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a|
|System.Drawing|System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a|

### どのアセンブリを使っているのか
例えば、「System.Security.Cryptography.SHA256Managed」は、どのdllなのか？

マイクロソフトのdotnetのapiサイトで検索、バージョンを「.NET Framework 4.8.1」を指定する。
SHA256Managed クラスのところに、アセンブリ:mscorlib.dll と記載されている。
https://learn.microsoft.com/ja-jp/dotnet/api/system.security.cryptography.sha256managed?view=netframework-4.8.1

```
SN.exe -T "mscorlib.dll"
公開キー トークン b77a5c561934e089
```

System.dll と同じなので、下記を使用すればよい。

```
Dim Assem As mscorlib.Assembly
host.CLRLoadAssembly ("System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")
```

# 最後に
この方法を使えば、 .NET Framework 4の多くの機能を利用出来るようになります。
2018年頃に公開されたライブラリですが、遅ればせながら紹介させて頂きました。

Excel には Mac版もあるので、あくまで Windows限定になります。

vb2clr の他に vb2net ライブラリがあり、これは .NET(旧.NET Core)を利用できるようになっています。これなら Mac版でも動作するかも知れませんね。
[VBから.NET(旧.NET Core)を利用してみる](https://www.pg-fl.jp/program/tips/vb2net.htm)