● システムメニューに独自メニューを追加する ●

Windows 95 と Windows XP で動作が異なる。新規メニューアイテムが追加される位置が違うようだ。

ここでは Windows XP 用として記述する。

'デフォルトのウィンドウ・プロシージャを呼ぶ
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

'ウィンドウの属性を設定する
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 GetSystemMenu Lib "user32" (ByVal hWnd As Long, ByVal bRevert As Long) As Long

'メニューを挿入する
Public Declare Function InsertMenu Lib "user32" Alias "InsertMenuA" (ByVal hMenu As Long, ByVal nPosition As Long, ByVal wFlags As Long, ByVal wIDNewItem As Long, ByVal lpNewItem As String) As Long
Public Declare Function AppendMenu Lib "user32" Alias "AppendMenuA" (ByVal hMenu As Long, ByVal wFlags As Long, ByVal wIDNewItem As Long, ByVal lpNewItem As Any) As Long

Public Const MF_BYCOMMAND = &H0&
Public Const MF_BYPOSITION = &H400&
Public Const MF_STRING = &H0&
Public Const MF_SEPARATOR = &H800&

Public Const SC_SIZE = &HF000       'サイズ変更
Public Const SC_MOVE = &HF010       '移動
Public Const SC_MINIMIZE = &HF020   '最小化
Public Const SC_MAXIMIZE = &HF030   '最大化
Public Const SC_CLOSE = &HF060      '閉じる
Public Const SC_RESTORE = &HF120    '元に戻す

Public Const GWL_WNDPROC = -4

Public Const WM_SYSCOMMAND = &H112

'独自の定数で"InsertMenu"関数の第4引数で使用する。
'&HF000より小さい任意の定数を設定しなければならない。
Public Const MY_MENU1 = &H1000
Public Const MY_MENU2 = &H1001

Public OldWindowhWnd As Long

'-----------------------------------------------------------------------
' 関数名 : SubClass
' 機能   : サブクラス化を開始する
' 引数   : (in) hWnd  … コールバック対象となるウインドウのハンドル
' 戻り値 : なし
'-----------------------------------------------------------------------
Public Function SubClass(ByVal hWnd As Long) As Long
    OldWindowhWnd = SetWindowLong(hWnd, GWL_WNDPROC, AddressOf WindowProc)
    SubClass = OldWindowhWnd
End Function


'-----------------------------------------------------------------------
' 関数名 : UnSubClass
' 機能   : サブクラス化を終了する
' 引数   : (in) hWnd  … コールバック対象となるウインドウのハンドル
' 戻り値 : なし
'-----------------------------------------------------------------------
Public Sub UnSubClass(ByVal hWnd As Long)
    Dim FuncRet As Long

    If OldWindowhWnd <> 0 Then
        '元のプロシージャアドレスに設定する
        FuncRet = SetWindowLong(hWnd, GWL_WNDPROC, OldWindowhWnd)
        OldWindowhWnd = 0
    End If
End Sub

'-----------------------------------------------------------------------
' 関数名:WindowProc
' 機  能:いわずとしれたウインドウプロシージャ
'-----------------------------------------------------------------------
Private Function WindowProc(ByVal hWnd As Long, ByVal uMsg As Long, _
                      ByVal wParam As Long, ByVal lParam As Long) As Long

    Select Case uMsg
        Case WM_SYSCOMMAND
            Select Case wParam
                Case MY_MENU1
                    Call MsgBox("メニュー1が選択されました")
                Case MY_MENU2
                    Call MsgBox("メニュー2が選択されました")
            End Select
    End Select

    WindowProc = CallWindowProc(OldWindowhWnd, hWnd, uMsg, wParam, lParam)

End Function

以下、面倒なので Form_Load() イベントに記述するということで(あまりよろしくは無いが…)。メニューの追加には InsertMenu() 関数、AppendMenu() 関数のどちらも使える模様。好みと流行に応じて使い分けましょう。私はどちらが流行っているかは知りません。

真面目に書くと AppendMenu() は追加位置は必ず最後尾となる。一方 InsertMenu() は第2引数で追加位置を指定できるからこちらの方が使いやすいかもしれない。と言ったものの、追加位置を指定しても最後尾に追加されてしまう。何でじゃ?第3引数に MF_BYPOSITION を指定して、第2引数に追加位置を数字で指定したらその指定位置にメニューが追加されたが…。

#素直に InsertMenuItem() 関数を使った方が良いのか?

Private Sub Form_Load()

    Dim hSystemMenu As Long

    'システムメニューのハンドルを取得
    hSystemMenu = GetSystemMenu(Me.hWnd, False)

    '※※※ MF_BYCOMMAND を指定する場合 - 開始 ※※※
    '区切り線を挿入
    Call InsertMenu(hSystemMenu, SC_CLOSE, MF_BYCOMMAND Or MF_SEPARATOR, 0, vbNullString)

    '独自メニューを挿入
    Call InsertMenu(hSystemMenu, SC_CLOSE, MF_BYCOMMAND Or MF_STRING, MY_MENU1, "メニュー1(&1)")
    Call InsertMenu(hSystemMenu, SC_CLOSE, MF_BYCOMMAND Or MF_STRING, MY_MENU2, "メニュー2(&2)")
    '※※※ MF_BYCOMMAND を指定する場合 - 終了 ※※※

    '※※※ MF_BYPOSITION を指定する場合 - 開始 ※※※
    '区切り線を挿入
    'Call InsertMenu(hSystemMenu, 2, MF_BYPOSITION Or MF_SEPARATOR, 0, vbNullString)

    '独自メニューを挿入
    'Call InsertMenu(hSystemMenu, 3, MF_BYPOSITION Or MF_STRING, MY_MENU1, "メニュー1(&1)")
    'Call InsertMenu(hSystemMenu, 4, MF_BYPOSITION Or MF_STRING, MY_MENU2, "メニュー2(&2)")

    '区切り線を挿入
    'Call InsertMenu(hSystemMenu, 5, MF_BYPOSITION Or MF_SEPARATOR, 0, vbNullString)
    '※※※ MF_BYPOSITION を指定する場合 - 終了 ※※※

    '※※※ AppendMenu() 関数を使う場合 - 開始 ※※※
    '区切り線を挿入
    'Call AppendMenu(hSystemMenu, MF_BYCOMMAND Or MF_SEPARATOR, 0, vbNullString)

    '独自メニューを挿入
    'Call AppendMenu(hSystemMenu, MF_BYCOMMAND Or MF_STRING, MY_MENU1, "メニュー1(&1)")
    'Call AppendMenu(hSystemMenu, MF_BYCOMMAND Or MF_STRING, MY_MENU2, "メニュー2(&2)")
    '※※※ AppendMenu() 関数を使う場合 - 終了 ※※※

    Call SubClass(Me.hWnd)
End Sub

Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
   Call UnSubClass(Me.hWnd)
End Sub

戻る