ビルドンブング

自由でシンプルな生活を求めて試行錯誤する記録を毎日更新中

【VBA × WindowsAPI】対になるAPIをPropertyにしてしまおう

当ブログではアフィリエイト広告を利用しています

VBAに限らず、WindowsAPIでいろいろ(主に他のアプリを操作)しようと思ったら、対象をGet〜で取得したり、対象にSet〜を行うだろう。

アクティブなウインドウを切り替えるためにSetForegroundWindowしたり、アクティブなウインドウを取得するのにGetForegroundWindowしたり。
といった具合に、何かとSetとGetでついになる関数がいくつもある。

これ、そのまんまVBAのプロパティにしてしまったら楽なのでは?
というわけで、例に挙げたForegroundWindowならこんな感じになる。
※以降に載せるコードについては全てWindowsAPIの宣言は省略している

Property Get Foreground() As LongPtr
    Foreground = GetForegroundWindow
End Property
Property Let Foreground(ByVal hWnd As LongPtr)
    SetForegroundWindow hWnd
End Property

これを使えば、「Foreground = hWnd」で対象のウインドウをフォアグラウンドにしたり、逆に「hWnd = Foreground」でhWndにアクティブなウインドウのハンドルを代入することができる。
いちいちGetForegroundWindowとSetForegroundWindowを使い分けるよりずっと手軽である。

まあ、ForegroundWindowについてはウインドウハンドルを取得するか指定するかくらいなので、わざわざプロパティにするほどのものでもないように思うだろう。

「WM_SETTEXT」と「WM_GETTEXT」なんかだと楽になる。

Property Get WindowText(hWnd As LongPtr) As String
    Dim sText As String: sText = String(255, Chr(0))
    Call SendMessageStr(hWnd, WM_GETTEXT, Len(sText), sText)
    Do While Right(sText, 1) = Chr(0)
        sText = Left(sText, Len(sText) - 1)
    Loop
    WindowText = sText
End Property
Property Let WindowText(hWnd As LongPtr, sText As String)
    Call SendMessageStr(hWnd, WM_SETTEXT, 0, sText)
End Property

いちいちSendMessageを呼び出して、ウインドウメッセージとパラメータを指定した関数を呼び出すより楽だ。

Set〜とGet〜でついになる関数があるようなAPIはプロパティに落とし込めば、それ以降のコーディングが楽になるし、何か間違った時はそのプロパティを直せば済む。

少し凝ったところでは、キーの押下状態なんていかがだろうか。

Property Get KeyPress(VK_ As Long) As Boolean
    If GetAsyncKeyState(VK_) = 0 Then
        KeyPress = False
    Else
        KeyPress = True
    End If
End Property
Property Let KeyPress(VK_ As Long, IsPress As Boolean)
    If IsPress Then
        Call keybd_event(VK_, 0, 0, 0)
    Else
        Call keybd_event(VK_, 0, KEYEVENTF_KEYUP, 0)
    End If
    Func.DoesEvents
End Property

普通に仮装キーコードを引数として、Trueにすれば押下状態に、Falseで解除できる。
さらにGetで状態も取得できるから、VK_CONTROLを引数にして、Ctrlキーが押されているかどうかの判定もできるから、APIに関係なく、いろいろなところで装飾キーの押下状態によって処理を分岐させるのにも使える。

他にも、ユーザーフォームに擬似的なTopMostプロパティを作るような使い方も考えられるだろうか。

Property Get TopMost(hWnd As LongPtr) As Boolean
    Dim style As LongPtr
    style = GetWindowLong(hWnd, GWL_EXSTYLE)
    If style = (style Or WS_EX_TOPMOST) Then
        TopMost = True
    Else
        TopMost = False
    End If
End Property
Property Let TopMost(hWnd As LongPtr, bIsTopMost As Boolean)
    If bIsTopMost Then
        SetWindowPos hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE Or SWP_NOSIZE
    Else
        SetWindowPos hWnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE Or SWP_NOSIZE
    End If
End Property

ユーザーフォームのウインドウハンドルは別で関数を作っておけば楽だ。

Function hwndForm(fm As Object) As LongPtr
    hwndForm = FindWindow("ThunderDFrame", fm.Caption)
End Function

別に一体化させてプロパティの引数をObjectにして中でハンドルを取得してもいいけれど、ユーザーフォーム以外のTopMostも操作できるように、ハンドルの取得と状態の設定を分けている。

書こうと思えばまだまだあるけれど、大体のイメージを掴めればいくらでも応用できるだろう。
別にAPIに限らず、プロパティは作ってしまえばいろいろと使い回せて便利である。
しかし、VBAを触り始めた頃はなかなかプロパティというものを理解できず、そもそも知る機会もなかったりして、ある程度成熟してから知ったりする。
その時に、今まで作った関数なんかを「プロパティにしておけばよかったー」と後悔したのが私だ。
早い段階でPropertyを使えるようになっておいて損はないだろう。