Visual Basic で Windows メッセージ横取り


 Visual Basic は Windows メッセージをイベントプロシージャとして扱っているので、以前は決まったイベントに対する処理しか書けませんでした。よって、ユーザ定義メッセージ等を使いたい場合でも、別の方法を考えなくてはなりませんでした。しかし、Visual Basic 5.0 から AddressOf 演算子が出来て Visual Basic から呼び出せる API の種類が増えました。従って、Visual C++ でしか出来なかった Windows メッセージの横取りが Visual Basic でも出来るようになり、ユーザ定義メッセージも使えるようになりました。今回は、以前コールバック関数の時にユーザ定義メッセージが受信できるかどうかを取り上げましたが、その答えとも言えます。

 特定のメッセージの受信方法は Visual C++ で行う時と同じです。SetWindowLong と CallWindowProc の2つの API を使います。

 まず、先の2つの API を定義します。標準モジュールの General Declarations に

    Public Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" _
      (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
    Public Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" _
      (ByVal lpPrevWndFunc As Long, ByVal hwnd As Long, ByVal MSG As Long, _
      ByVal wParam As Long, ByVal lParam As Long) As Long

と書きます。この辺は DLL の関数の定義などと同じです。次に、メッセージハンドラ、即ち、メッセージを受信したときに呼び出される関数を定義します。この関数もコールバック関数です。API の SetWindowLong でメッセージを受信したい関数を Windows に知らせて、処理後は CallWindowProc で標準のメッセージハンドラを呼び出します。


    'メッセージディスパッチャ
    Public Function VBWindowProc(ByVal hwnd As Long, ByVal uMsg As Long, _
      ByVal wParam As Long, ByVal lParam As Long) As Long

      Static blnProcRunning As Boolean

      If blnProcRunning = False Then
        blnProcRunning = True
        Select Case uMsg
          Case WM_USER + 1
            '何かの処理
          Case WM_USER + 2
            '何かの処理
        End Select
        blnProcRunning = False
      End If

      '標準のメッセージハンドラを呼び出す
      VBWindowProc = CallWindowProc(lngOriginalProc, hwnd, uMsg, wParam, lParam)

    End Function

 注意する点としては、blnProcRunning の使い方のでしょうか。一見すると、何のためにあるのか不明かもしれませんが、Static 変数であることに注目していください。blnProcRunning はメッセージが送信されるたびに呼び出されるので、この様な作りになってます。blnProcRunning 変数があっても無くても変わらないように、VBWindowProc 内では重たい処理をせずに、さっさと CallWindowProc 関数を呼び出して標準のメッセージハンドラに処理を返さないと、プログラムが変な動きをするときがあります。僕はこの処理を使ったとき、メッセージディスパッチャ内では、Collection にメッセージIDと wParam と lParam の情報をセットしてさっさとリターンし、タイマイベントで Collection の情報から各メッセージに対する処理をしました。この方法だと、多少のタイムラグが発生しますが、安定した動作には代えられません。。。

 次に、自分で作ったメッセージハンドラを Windows に教えます。

    Const GWL_WNDPROC = -4

    'デフォルトのメッセージハンドラを変更する
    lngOriginalProc = SetWindowLong(Me.hwnd, GWL_WNDPROC, AddressOf VBWindowProc)

AddressOf がここで登場しました。lngOriginalProc は接頭語の通り、Long 型の変数です。戻り値はデフォルトのウィンドウハンドラです。プログラムを終了する前は、次のようにしてデフォルトのウィンドウハンドラを元に戻しておきます。

    'デフォルトのメッセージハンドラを元に戻す
    lngOriginalProc = SetWindowLong(Me.hwnd, GWL_WNDPROC, lngOriginalProc)

以上でOK。Visual Basic の安定性はいまいちになるかも知れません。ま、ややこしい処理がしたければ、やはり Visual C++ で作った方がいいようですが。。。 あと、サービスパックは充てた方が何かと賢明です、はい。


プログラム研究所 トップ

TOP-PAGE

時工房制作者e-mailアドレス