Hidennotareの紹介

#Hidennotare とは

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69810/3314f491-5a62-80d7-5241-bcf75a28cbf7.png)

「Hidennotare」は なるべくクラスの再利用性を上げるべく開発されたVBAのクラスライブラリです。
Excel2010以上の32/64bitに対応。Excel2007以前には対応していません。

Hidennotare
https://github.com/RelaxTools/Hidennotare

Hidennotare Wiki
https://github.com/RelaxTools/Hidennotare/wiki

VBAは継承が使用できないため、移譲やインタフェースによるポリモーフィズムをメインとしています。
インターフェースって便利なの？というVBAユーザの参考にもなるかと思います。

##直接アクセスできるクラス

通常クラスはインスタンスを生成(New または CreateObject)しないと使えませんが、特にインスタンスを必要としないような関数のみのクラス(FileIO, RegExp 等）はそのまま使えるようになっています。

```vb

    'Scripting.FileSystemObject をラップした、FileIO クラスのメソッドは標準モジュールのように使える。
    FileIO.FolderCreate TargetPath

    'VBScript.RegExp をラップした、RegExp クラスも同様
    blnRet = RegExp.Test(strFile,"[A-Z0-9]*")
```

##直感的に書けるコンストラクタ

インスタンスが必要なクラスで、引数が必要なものはコンストラクタが使えます。
コンストラクタの名前は「CreateObject」です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69810/15ff3245-c89f-6da6-c398-bf63698f9865.png)

インテリセンスで引数の内容が表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69810/5fa6b2ff-86bd-9fa5-a35a-73f4b83c8af3.png)


##ポリモーフィズム

Hidennotare は変更に強くするために ポリモーフィズムを多用しています。
たとえば、ICursor インターフェース(以下I/F)に対応しているクラスはすべて同じ動作で処理をすることが可能です。
ICurosr I/F に対応したTextファイルを読む処理を作成した場合、簡単に他の処理（CSVファイルを読む、Sheet を読む、配列を操作、連想配列を操作）に変更することが可能です。

###ICursor インターフェイス
#### ICursor I/F の使い方
ICursor で変数を宣言し、対応クラスのインスタンスを設定する。

```vb

    Dim cc As ICursor
    'コンストラクタ    
    Set cc = TextReader.CreateObject("File.txt")
```

クラスによっては ICursor I/F を返すものもあるので With で使用可能です。この辺はWiki を見るかソースで確認してください。

```vb
    'コンストラクタ    
    With TextReader.CreateObject("File.txt")
```

#### ICursor I/F に対応したクラス

* CharCursor
* CollectionCursor
* RangeCursor
* SheetCursor
* TableCursor
* TextReader
* CSVReader
* ArrayList
* LinkedList
* MList
* Dictionary
* OrderedDictionary
* SortedDictionary
* MRecord


以下はシートを読むクラスですが、他のCSVファイルを読む機能に変更する場合、コンストラクタを

```vb
    Dim cc As ICursor
    'コンストラクタ    
    Set cc = SheetCursor.CreateObject(Sheet1, 3, "B")
```
から

```vb
    Dim cc As ICursor
    'コンストラクタ    
    Set cc = CSVReader.CreateObject("File.csv")
```

に書き換えるだけで、それ以降はほぼ修正が無くても行けます。（一部例外あり）

####ICursor インターフェース の有用性

なぜ ICursor I/F を使う必要があるのかとなるのですが、VBA の各クラスの I/F が統一されていないためです。
また、標準の For Each も問題があります。
今の言語は必ずIterator(For Each)があってとても簡単にループが作成できて便利ですが、For Each を使って集計処理をすると以下のようなロジックになってしまいます。

#####For Each を使用した集計処理の場合

```vb
    Dim col As Collection
    Dim v As Variant
    Dim strWork As String
    Dim lngCnt As Long
    
    Set col = New Collection
    
    col.Add "あ"
    col.Add "い"
    col.Add "い"
    col.Add "う"
    col.Add "え"
    col.Add "え"
    col.Add "お"
    col.Add "お"

    strWork = ""
    lngCnt = 0
    
    For Each v In col
    
        If v <> strWork Then
            
            '初回
            If strWork <> "" Then
                Debug.Print strWork & lngCnt
            End If
        
            strWork = v
            lngCnt = 0
        
        End If
        
        lngCnt = lngCnt + 1
    
    Next

    '集計結果を表示する前にループを抜けてしまうので同じロジックを書く必要が出てしまう。
    If v <> strWork Then
        Debug.Print strWork & lngCnt
    End If
```

集計場所が複数になり、キーが増えるほど複雑な処理になっていく。

##### EOF/MoveNextでのループ(ICursor) を用いた集計処理の場合

```vb
    Dim col As Collection
    Dim v As Variant
    Dim strWork As String
    Dim lngCnt As Long
    
    Set col = New Collection
    
    col.Add "あ"
    col.Add "い"
    col.Add "い"
    col.Add "う"
    col.Add "え"
    col.Add "え"
    col.Add "お"
    col.Add "お"
    
    Dim cc As ICursor

    'コンストラクタ    
    Set cc = CollectionCursor.CreateObject(Col)
    
    Do Until cc.Eof
    
        '初期化
        strWork = cc.item
        lngCnt = 0
            
        '集計処理
        Do Until cc.Eof Or strWork <> cc.item
    
            lngCnt = lngCnt + 1
            
            cc.MoveNext
        Loop
        
        '集計結果
        Debug.Print strWork & lngCnt
        
   Loop
```

複数ループ（集計単位）があってもループの中心で次のデータに移動(MoveNext)できるため、

* 初期化処理
* 集計処理
* 結果表示

をわかりやすく記述することができます。

###IList インターフェイス

配列の操作を行うインターフェイスです。変数を IList で宣言し、IList I/F に対応するクラスのインスタンスを設定します。

```vb
    Dim Row As IList
    Set Row = New ArrayList
```

#### IList I/F に対応したクラス

* ArrayList
* LinkedList
* MList

###IDictionary インターフェイス

連想配列の操作を行うインターフェイスです。変数を IDictionaryで宣言し、IDictionaryI/F に対応するクラスのインスタンスを設定します。

```vb
    Dim Col As IDictionary
    Set Col = New Dictionary
```

#### IDictionaryI/F に対応したクラス

* Dictionary※
* OrderedDictionary
* SortedDictionary
* MRecord

※Scripting.Dictionary とは別の独自クラスです。

## その他 インターフェイス

ほかにもインターフェイスかいろいろあります。

* IComparer
* ICompatibleProperty
* INewInstance
* IUsing
* IReader
* IWriter

##その他 Hidennotare の便利な機能

###配列の操作を楽にするArrayList

1次元配列を扱う際に、Redim や Preserve を使用する必要はありません。ToArray() メソッドで変換するだけです。

```vb
    Dim ar As IList

    Set ar = New ArrayList

     ar.Add "あ"
     ar.Add "い"
     ar.Add "う"

     ar.ToArray()
```

###単純な文字列連結からコマンド生成まで自由自在のStringBuilder

Hidennotare で最も使われるクラスです。

#### 文字列を連結

SQL コマンドや CSV ファイルの組み立てが楽になります。

```vb
    Set SB = New StringBuilder

    SB.Append "Column01"
    SB.Append "Column02"
    SB.Append "Column03"
    SB.Append "Column04"
    SB.Append "Column05"

    SB.ToString(",")
```

組み立てたコマンドの内容

```vb
    Column01,Column02,Column03,Column04,Column05
```

####コマンド文字列を作成

DOS コマンドや、Power シェルのコマンドを組み立てる際に、ToString(" ") を使用すると追加した文字列を空白で連結することが可能です。また、Append の第2引数を True にすると追加した文字列をダブルコーテーションで囲むことができます。

```vb
    Set SB = New StringBuilder
    
    'コマンド
    SB.Append "Compress-Archive"
    
    '圧縮するファイル
    SB.Append "-Path"
    Set PH = New StringBuilder
    For Each v In ary
    
        PH.Append v, True
    
    Next
    SB.Append PH.ToString(",")
    
    '出力パス
    SB.Append "-DestinationPath"
    SB.Append strDest, True

    SB.Append "-Force"
    
    'PowerShell を実行する
    Process.ExecPowerShell SB.ToString(" ")
```

組み立てたコマンドの内容

```
    Compress-Archive -Path "C:\Test.csv","C:\Test2.Txt" -DestinationPath "C:\Test.zip" -Force
```

###Windows API は 64bit、Unicode 対応

内部で Windows API を使用していますが、 32, 64bit 両対応、WCHAR 系の API を使用しているためファイル名やクリップボードにUNICODE文字列を使用していても問題なく動作します。

```vb
Private Declare PtrSafe Function FindFirstFileExW Lib "kernel32" (ByVal lpFileName As LongPtr, ByVal fInfoLevelId As Long, ByRef lpFindFileData As WIN32_FIND_DATA, ByVal fSearchOp As Long, ByVal lpSearchFilter As LongPtr, ByVal dwAdditionalFlags As Long) As LongPtr
Private Declare PtrSafe Function FindFirstFileW Lib "kernel32" (ByVal lpFileName As LongPtr, lpFindFileData As WIN32_FIND_DATA) As LongPtr
Private Declare PtrSafe Function FindNextFileW Lib "kernel32" (ByVal hFindFile As LongPtr, lpFindFileData As WIN32_FIND_DATA) As LongPtr
Private Declare PtrSafe Function CreateFileW Lib "kernel32" (ByVal lpFileName As LongPtr, ByVal dwDesiredAccess As Long, ByVal dwShareMode As Long, ByVal lpSecurityAttributes As LongPtr, ByVal dwCreationDisposition As Long, ByVal dwFlagsAndAttributes As Long, ByVal hTemplateFile As LongPtr) As LongPtr
Private Declare PtrSafe Function WNetGetConnectionW Lib "mpr" (ByVal lpszLocalName As LongPtr, ByVal lpszRemoteName As LongPtr, lSize As Long) As Long
Private Declare PtrSafe Function SearchTreeForFileW Lib "dbghelp" (ByVal RootPath As LongPtr, ByVal InputPathName As LongPtr, ByVal OutputPathBuffer As LongPtr) As Long
Private Declare PtrSafe Function StrCmpLogicalW Lib "Shlwapi" (ByVal psz1 As LongPtr, ByVal psz2 As LongPtr) As Long
Private Declare PtrSafe Function DragQueryFileW Lib "shell32.dll" (ByVal hDrop As LongPtr, ByVal UINT As Long, ByVal lpszFile As LongPtr, ByVal ch As Long) As Long
```

###JSON 対応

JSON 文字列 -> ArrayList + Dictionary※ または ArrayList + Dictionary※ -> JSON 文字列が可能。
ユーザが作成した データ保持クラスも指定可能です。
※Scripting.Dictionary とは別の独自クラスです。

####シリアライズ化

ArrayList + Dictionary -> JSON 文字列

```vb
    Dim Row As IList
    Dim col As IDictionary
    
    Set Row = New ArrayList
    Set col = New Dictionary
    col.Add "Field01", 10
    col.Add "Field02", 20
    col.Add "Field03", 30
    Row.Add col

    Set col = New Dictionary
    col.Add "Field01", 40
    col.Add "Field02", 50
    col.Add "Field03", 60
    Row.Add col
    
    Debug.Print Row.ToString
```

Row.ToString の結果

```
[{"Field01":10, "Field02":20, "Field03":30}, {"Field01":40, "Field02":50, "Field03":60}]
```

####デシリアライズ化

JSON 文字列 -> ArrayList+Dictionary

```vb
    Dim Row As IList
    Dim Col As IDictionary
    Dim Key As Variant

    Set Row = Parser.ParseJSON("[{""Field01"":10, ""Field02"":20, ""Field03"":30}, {""Field01"":40, ""Field02"":50, ""Field03"":60}]")

    For Each Col In Row
        For Each Key In Col
            Debug.Print "Key=" & Key
            Debug.Print "Value=" & Col.Item(Key)
        Next
    Next
```

ArrayList/Dictionary の内容

```
Key=Field01
Value=10
Key=Field02
Value=20
Key=Field03
Value=30
Key=Field01
Value=40
Key=Field02
Value=50
Key=Field03
Value=60
```

###メッセージのプレースホルダー化と制御文字

メッセージを表示する際に「\n」などの制御文字、埋め込み文字列が可能なプレースホルダ。ステータスバーへのメッセージなど機能があります。

```vb
    'Information メッセージ
    Message.Information "サンプルです。"

    '改行する場合
    Message.Information "サンプルです。\n改行も簡単に使えます。"

    'リプレースホルダを使用する場合
    Message.Error "サンプルです。{0}のだけでなく{1}もある", "金", "名誉"
    
    'ステータスバー
    Message.StatusBar "サンプルです。{0}のだけでなく{1}もある", "金", "名誉"

```


以上です。使ってみてください。

Hidennotare
https://github.com/RelaxTools/Hidennotare

Hidennotare Wiki
https://github.com/RelaxTools/Hidennotare/wiki