t-hom’s diary

主にVBAネタを扱っているブログ…とも言えなくなってきたこの頃。

VBA MsgBoxのオプションはなぜ足し算できるのか

VBAのメッセージボックスでは、以下のように適用したいスタイルを足し算できる。

Sub hoge()
    MsgBox "処理を続行しますか。", vbYesNo + vbQuestion + vbDefaultButton2, "確認"
End Sub

上記のマクロを実行すると、このように表示される。
f:id:t-hom:20170108105506p:plain

変な文法だと思われた方もいるだろう。
MsgBoxは関数である。ふつう関数に引数を複数渡そうと思ったら、ひとつずつカンマ区切りで渡す。
ただ上の例では「vbYesNo + vbQuestion + vbDefaultButton2」と足し算している。

今回はこれの仕組みを解説しようと思う。

MsgBoxでオプションを足し算できる仕組み

まず、それぞれの定数の値をイミディエイトウインドウで確認してみると、4、32、256と出る。
f:id:t-hom:20170108104916p:plain

全て足し合わせると、292だ。では292を直接渡してみよう。

Sub hoge()
    MsgBox "処理を続行しますか。", 292, "確認"
End Sub

これでも同じ表示になる。

実はMsgBoxに指定できる定数値は以下のようになっており、これらを一つずつ組み合わせて292を作ろうと思ったら、必然的に「256=vbDefaultButton2」、「32=vbQuestion」、「4=vbYesNo」の組み合わせになるのだ。

定数
vbApplicationModal 0
vbDefaultButton1 0
vbOKOnly 0
vbOKCancel 1
vbAbortRetryIgnore 2
vbYesNoCancel 3
vbYesNo 4
vbRetryCancel 5
vbCritical 16
vbQuestion 32
vbExclamation 48
vbInformation 64
vbDefaultButton2 256
vbDefaultButton3 512
vbDefaultButton4 768
vbSystemModal 4096
vbMsgBoxHelpButton 16384
vbMsgBoxSetForeground 65536
vbMsgBoxRight 524288
vbMsgBoxRtlReading 1048576

つまり足し合わせて渡しても、定数表をもとに分解できるということ。
定数値を1、2、4、8、16と倍々にしていくと、それらの組み合わせは元の定数に分解できるのだ。
※一部48や768などの2のn乗になっていないものがあるが、これは32+16、512+256の組み合わせになっている。2進数を学習すると意味が分かると思うのでここでは説明を割愛。

ただし、同じカテゴリのプロパティを足し合わせるとおかしなことになる。

たとえば以下のようにすると、32+16だから、48=vbExclamationと同じことになる。

Sub hoge()
    MsgBox "Hello", vbQuestion + vbCritical
End Sub

こうすると、

Sub hoge()
    MsgBox "Hello", vbYesNo + vbYesNo + vbYesNo + vbYesNo
End Sub

こうなる。
f:id:t-hom:20170108114233p:plain

4+4+4+4=16=vbCriticalなので。

自分で同じ仕組みを作って理解する。

さて、では列挙型を使用して自分で同じ仕組みを作ってみよう。
今回は人物の特徴を題材にコードを書いてみた。

まずはEnumで列挙型定数を宣言する。

Enum 人物の特徴
    男性 = 1
    女性 = 2
    裸眼 = 4
    コンタクト = 8
    メガネ = 16
    スリム = 32
    ふくよか = 64
    A型 = 128
    B型 = 256
    AB型 = A型 + B型
    O型 = 512
End Enum

そして、人物の特徴を表示させるためのプロシージャを用意。

Sub 表示(T As 人物の特徴)
    Dim 血液型 As String
    If T >= O型 Then
        血液型 = "O型"
        T = T - O型
    ElseIf T >= AB型 Then
        血液型 = "AB型"
        T = T - AB型
    ElseIf T >= B型 Then
        血液型 = "B型"
        T = T - B型
    Else
        血液型 = "不明"
    End If
    
    Dim 体型 As String
    If T >= ふくよか Then
        体型 = "ふくよか"
        T = T - ふくよか
    ElseIf T >= スリム Then
        体型 = "スリム"
        T = T - スリム
    Else
        体型 = "標準"
    End If
    
    Dim 視力矯正 As String
    If T >= メガネ Then
        視力矯正 = "メガネ"
        T = T - メガネ
    ElseIf T >= コンタクト Then
        視力矯正 = "コンタクト"
        T = T - コンタクト
    ElseIf T >= 裸眼 Then
        視力矯正 = "裸眼"
        T = T - 裸眼
    Else
        視力矯正 = "不明"
    End If
    
    Dim 性別 As String
    If T >= 女性 Then
        性別 = "女性"
        T = T - 女性
    ElseIf T >= 男性 Then
        性別 = "男性"
        T = T - 男性
    End If
    
    MsgBox "この人物の特徴" & vbNewLine _
        & "性別:" & 性別 & vbNewLine _
        & "視力矯正:" & 視力矯正 & vbNewLine _
        & "体型:" & 体型 & vbNewLine _
        & "血液型:" & 血液型
End Sub

このプロシージャでは人物の特徴を大きな値のものから探し、見つかったらその値を引く。残った数値からまた大きな値を探して引くという繰り返しでデータを分解していく。

では実際に呼び出してみよう。

Sub Main()
    表示 メガネ + 男性 + ふくよか + AB型
End Sub

実行すると、足し合わせて渡した値が正しく表示されているのがわかる。
f:id:t-hom:20170108113747p:plain

このように合計値を数値で渡しても同じ結果になる。

Sub Main()
    表示 465
End Sub

以上がメッセージボックスのオプションが足し算できる仕組みである。
もしこの仕組みをより詳しく知りたければ、今回私が書いたコードをそのままVBEで手入力してみると良い。入力候補が適切に表示され、VBエディタが定数の計算をよくアシストしてくれるのがわかる。

当ブログは、amazon.co.jpを宣伝しリンクすることによってサイトが紹介料を獲得できる手段を提供することを目的に設定されたアフィリエイト宣伝プログラムである、 Amazonアソシエイト・プログラムの参加者です。