【VBS】.NETを使用した書式指定(Format)の文字列を取得する

# はじめに
これは、[Visual Basic Advent Calendar 2019](https://qiita.com/advent-calendar/2019/vb)の9日目の記事となります。

# Format について
VBScriptを使用していて不満点が多くあるけど、その中でもFormat関数が貧弱なところがとても不便です。
下記のようにFormatXXXXXと機能ごとに分割されてしまっている。

FormatDateTime関数
FormatCurrency 関数
FormatNumber 関数
FormatPercent 関数

FormatDateTime関数は日時を書式文字列化して返却する関数ですが、時刻が0埋めにならないなど使いにくい。

自分でも下記記事を書いてみたりしました。

* [【VBS】Format関数の@書式を作ってみた](https://qiita.com/yaju/items/8e07c8c6d9eeda316cd6)
* [【VBS】PowerShellを使用した日時書式変換](https://qiita.com/yaju/items/81dc2d396c27305e4034)

.NETのFormat関数を呼び出して使えるのが一番いいのですが、使えないと思い込んでいて PowerShell を呼び出してみるなど試していました。最近ネットで見つけたのが下記サイトになります。

>CreateObjectで.NETのStringクラスを作ればString.Formatを使える……、と思ったけどStringクラスは利用できず。
だけどStringBuilderクラスは利用できた。StringBuilderのAppendFormatメソッドならFormat関数の代わりになる。
[VBScriptでのFormat関数の代わり (書式文字列を使いたい)](http://blog.livedoor.jp/tea_cocoa_cake/archives/7578885.html)

```vb
' StringBuilderクラスのインスタンスを生成
dim sb1: set sb1 = CreateObject("System.Text.StringBuilder")

' 配列で指定した値(1, "now", now) を
' それぞれ<デフォルト>, <幅8文字分>, <幅12文字分, yyyy/MM/dd>の書式で
' StringBuilderに追加
sb1.AppendFormat_4 "{0}, {1,8}:{2,12:yyyy/MM/dd}",Array(1, "now", now)

' StringBuilderを文字列に変換
wscript.echo sb1.toString 
```

AppendFormatの使い方や書式指定子の書き方はSprintfに似ている。
これを使えば、日付時刻書式、文字数と右寄せ・左寄せや3桁区切り、小数の表示桁数、ゼロパディングが使える。
※和暦には対応できていないようです。

「CreateObject("System.Text.StringBuilder")」で検索してみたら、いろいろ見つけた。

* [StringBuilderをvba用にラップしてみた](http://blog.livedoor.jp/midorityo/archives/50744656.html)
* [A Format function for VbScript](https://www.tek-tips.com/viewthread.cfm?qid=1570966)


# 仕組み
.NETのクラスの一部はCOM相互運用機能が実装されている。どれかはレジストリの「HKEY_CLASSES_ROOT」を参照する。
WSHではメソッドのoverloadなんて概念は存在しないでの、引数の数が違うだけの同名のメソッドは普通に叩くとエラーとなる。その場合はxxx_2のように最後にアンダースコア＋数字をくっつけて区別する

* [Microsoft TechNet 2007年1月号 HEY, SCRIPTING GUY!](https://web.archive.org/web/20070302220956/http://www.microsoft.com/technet/technetmag/issues/2007/01/HeyScriptingGuy/default.aspx)
* [スクリプトから使えるよう、.NETのオーバーロードのサフィックスを調べる。](http://scripting.cocolog-nifty.com/blog/2008/07/net_a024.html)
* [スクリプトから.NETのSystem.Text.StringBuilderを使う。](http://scripting.cocolog-nifty.com/blog/2008/07/netsystemtextst_8388.html)
* [連想配列にCollectionを使わない理由](http://www.10days.org/diary/20071115.html)
* [.NET Frameworkの素敵機能をJScriptで使いたい](https://masayoshi-9a7ee.hatenablog.com/entry/20160628/1467098711)

# 注意点
Windows 8、Windows 8.1、Windows Server 2012以降では、.NET 3.5 が最初からインストールされていません。
.NET 4.5 からは、.NET型をCOMインターフェイスとして公開することを停止したため、"System.Text.StringBuilder" や "System.Collections.ArrayList" などがエラーになるようになりました。
このため、Windows 10 ユーザーに配布するには注意が必要です。

対応としては、.NET 3.5をインストールすることで使えるようになります。
それ以外では、「cscript.exe.activation_config」という名前のアクティベーション設定ファイルを作成することで使用できるようです。

* [Usage of .NET Collections types in VBScript is not supported after .NET 4.5](https://blogs.msdn.microsoft.com/dsvc/2013/03/04/usage-of-net-collections-types-in-vbscript-is-not-supported-after-net-4-5/)
* [Windows10にしたらVBAから.Net Frameworkが使えなくなった](https://qiita.com/RelaxTools/items/ba06a14fe6865a418b06)
* [Windows10で実行したスクリプトでArrayListの作成がエラーになった](https://evergreen-nage.blog.ss-blog.jp/2018-06-16)
* [How can I run PowerShell with the .NET 4 runtime?](https://stackoverflow.com/questions/2094694/how-can-i-run-powershell-with-the-net-4-runtime)

# 最後に
VBScript内だけであれば簡単なFormatは自作してしまえばいいですが、バッチファイルで日時計算(9時間前計算)とか一時的にVBSを使用したい場合、この方法は便利です。

```bash
echo dim sb1:set sb1=CreateObject("System.Text.StringBuilder"):sb1.AppendFormat_4 "{0:yyyy-MM-ddTHH:mm:ss}",Array(DateAdd("h",-9,Replace("%1","T"," "))):WScript.Echo sb1.toString > tmp.vbs
for /f "tokens=1" %%A in ('cscript //nologo tmp.vbs') do set RETVAL=%%A
del tmp.vbs
```