VisualBasic6はなぜ死なないのか

## まえがき

もう殆どの方の目の前からはVB6は駆逐されたか幸いにも目にしたことがないかと思いますが私の職場ではVB6での新規開発は流石にありませんが機能追加・変更は時々あります。勿論VB6の面倒見るのをやめたいのは山々ですがなぜいまだにしぶとく生き残っているのか・生き残ってはならない理由など考えてみたいと思います。人[^1]と金[^2]の問題なのは良くわかっているのでプログラミング言語としてどうなの？という話です。

VB6は1998年にリリースされ今年で22年になります。VB6のMSのサポートは2008年に延長サポート期限が切れたので今使って何かあってもMSとしてはもう知らない状態の言語です。

## 生き残ってる理由(と言うか、これくらいは出来るでー)

### ActiveX.DLL/ActiveX.EXEが作れる

ActiveX.DLL/ActiveX.EXEはさほど使い勝手の良いものではありませんがDLL化することで資産の再利用が可能となっています。作成したDLLはネイティブではないのでレジストリ登録が必要です。

```Shell
> regsvr32 hoge.dll
```

### API呼び出し

WindowsAPIを呼び出して利用することができます。これでVBだけでは実現できないことが殆どできてしまいます。

```VB:APICall.cls
Private Declare Function FindWindow Lib "user32" _
　　Alias "FindWindowA" (ByVal lpClassName As String, _
　　ByVal lpWindowName As String) As Long

Private Declare Function SetForegroundWindow Lib "user32" _
　　(ByVal hWnd As Long) As Long

Private Sub foo(ByVal strClassName As String)
    Dim lngHwnd As Long

    lngHwnd = FindWindow(strClassName, vbNullString)
    Call SetForegroundWindow(lngHwnd)
End Sub
```

### DictionaryとCollection

Collectionは元々搭載されていますがDictionaryはMicrosoft Scripting Runtimeを参照設定することで利用できるようになります。Collectionはちょっと変わっていてキーと共にDictionary的な使い方もできますしAdd()する型は同じである必要はありません。ちなみにStackとかQueueとかはありませんしCollectionを配列に変換したかったらループさせて代入するしかないです[^3]。

### オブジェクト指向

オブジェクト指向の三大要素と言われる「継承」「カプセル化」「ポリモーフィズム」のうち「継承」は実装されていません。確かに継承が無いと殆ど一緒で一部だけ動きを変えたい複数のクラスに共通処理まで実装する必要があるのですがポリモーフィズムが無いよりはマシかなと思います。

「カプセル化」についてはスコープがPublicとPrivateしかありませんがまぁなんとか(汗) 併せてプロパティもありますのでなんとなくそれっぽくはなりますがちょっと貧弱です。Friendという修飾子もありますが、同一プロジェクトの中でのみアクセス可能となるのでC#で言うところのinternal相当でしょうか。

```VB:Capsule.cls
Private data As String

Public Property Get Hoge() As String
    Hoge = data
End ProPerty

Public Property Let Hoge(ByVal strValue As String)
    data = strValue
End Property
```

「ポリモーフィズム」についてはInterfaceが使えます。ただVB6管轄のオブジェクトにインターフェースというものはなく通常のクラスを用います。メソッドとプロパティのみインターフェースとして記述できます。

```VB:Monster.cls
' インターフェースとして扱うクラス
Public HP As Integer    '変数の宣言をしているように見えて実際はこの名称のget/setプロパティの宣言

Public Sub Skill()
End Sub
```

Monsterインターフェースを実装するAkineクラス

```VB:Akine.cls
Implements Monster      'Monsterインターフェースを実装する宣言

Private mHP As Integer

' プロパティ・メソッド名にインターフェース名が必ず付く
Private Property Let Monster_HP(ByVal RHS As Integer)
    mHP = RHS
End ProPerty

Private Property Get Monster_HP() As Integer
    Monster_HP = mHP
End Property

Private Sub Monster_Skill()
    Call MsgBox("蛇骨姫の龍鳴")
End Sub
```

同、Daiaクラス

```VB:Daia.cls
Implements Monster

Private mHP As Integer

Private Property Let Monster_HP(ByVal RHS As Integer)
    mHP = RHS
End ProPerty

Private Property Get Monster_HP() As Integer
    Monster_HP = mHP
End Property

' Akineクラスと異なるのはこのメソッドのみ
Private Sub Monster_Skill()
    Call MsgBox("神器宿装・アーウィル")
End Sub
```

呼び出し元。

```VB:MainForm.frm
'型指定のできないCollectionとして宣言してしまうと
'Monsterのサブクラスとして扱えなくなるので敢えてMonster型の配列で扱う
Private monsters() As Monster

Private Sub Form_Load()
    ReDim monsters(2)

    Dim m As Monster
    Set m = New Akine
    m.HP = 7497     'アクセスする時にはインターフェース名は不要
    Set monsters(1) = m

    Set m = New Daia
    m.HP = 3810
    Set monsters(2) = m

    Dim i As Integer
    For i = 1 To UBound(monsters())
        Call MsgBox("HP=" & monsters(i))        '「HP=7497」「HP=3810」が表示される
        Call monsters(i).Skill      '「蛇骨姫の龍鳴」「神器宿装・アーウィル」が表示される
    Next
End Sub
```

コメントにも書きましたがサブクラスは配列で扱う必要があるのでループするところでforeach的な処理はできません[^4]。また、インターフェースにイベントの宣言は書けない(書いても無視)ので各サブクラスからのイベント通知ができません。各サブクラスにイベントを書くことはできますがインターフェースに定義が無い以上は意味がないので発火できないです(何か別の手段を考える必要があります)

***

## あとがき(+生き残ってはならない理由・・・は言い過ぎ)

と、まぁこんな感じでプログラミング言語としては20年以上前の言語でそこから進化してないので昨今の「こんなめんどくさいこと書いてられるかよっ！」とか「こんな意図のわかりにくい実装書かすなよっ！」が何も解消されていませんが、そこそこ最低限は出来てしまうのでそれほど役立たずにならないのは生き残っている理由の一つと考えられます。が、言語仕様云々よりも操る人間の方の進化が止まってしまっている方が要因として大きいと思います。VB6やったらこう書くよねをC#のソースで見るにつけ悲しい気持ちになります・・・

もうあんまりVB6を書きたくない理由の一つは前記のサンプル見ればわかりますが兎に角書くコード量が多いことです。無いものを挙げればLINQ、型推論、ラムダ式、自動実装プロパティ、オブジェクト・コレクション初期化子など無いものばかりなので色々と一から書くものが多いです。これはもう車輪の再発明の領域に足を踏み入れてる感が。いや、LINQとかラムダ式の実装とかは無理ですが例えば

```VB:Queue.cls
Option Explicit

Private queueData As Collection

Private Sub Class_Initialize()
    Set queueData = New Collection
End Sub

Public Sub Enqueue(ByVal value As Variant)
    Call queueData.Add(value)
End Sub

Public Function Dequeue() As Variant
    If queueData.Count >= 1 Then
        Dequeue = queueData.Item(1)
        Call queueData.Remove(1)
    Else
        Dequeue = Null
    End If
End Function

Public Property Get Count() As Integer
    Count = queueData.Count
End Function
```

万事こんな調子で。

言語仕様も今となっては貧弱ですが冒頭に書いた人と金の方が問題かなとは思います。20代後半から30代前半の人がVB始めると20年強経つわけですから50才前後。もうこれくらいになると「今できることだけしたい！新しいことなんか覚える気はさらさらない！」という人もいます。Qiita覗きに来たり投稿したりする人はそんなことないでしょうがQiita？喰えるのか！？という人もいますから。また長い目で見れば乗り換えた方がクライアントも結局コスト抑えられるとは思うのですが目先の出費のことしか考えない人も多いわけで。

そんなこんなで負の遺産が末端エンジニアに降り注ぐのではよやめたいと願う今日この頃。

[^1]: VB6に慣れ切った50代のメンバがもう新しいことを学ぶのを本能的に拒否している
[^2]: VB6->C#とか変更するのはあくまでも開発側の都合と言われるためなかなか費用が出ない
[^3]: ToArray()とかは遠い国の話
[^4]: VB6では配列に対してのforeachはできません