● タスクトレイにアイコンを設定する - その2 ●

その昔、ランチャーを作った時に使ったなぁ。

コールバックを使用するのでソース量が多い。直接コールバックを使用しない方法に NOTIFYICONDATA 構造体のメンバ uCallbackMessage に WM_MOUSEMOVE を指定する方法があるが、それについてはこちらを参照。

Private Declare Function SetWindowLong Lib "User32" Alias "SetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long

Private 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

Private Const GWL_WNDPROC = (-4)

Private Type NOTIFYICONDATA
    cbSize As Long            '構造体のサイズ
    hWnd As Long              'マウスメッセージを受け取るウィンドウのハンドル
    uID As Long               'アイコンのID
    uFlags As Long            'フラグを組み合わせて指定する
    uCallbackMessage As Long  'マウスメッセージを受け取る時のメッセージを指定する
    hIcon As Long             '表示するアイコンハンドル
    szTip As String * 64      'ヒントメッセージ
End Type

Private udtNotifyIcon As NOTIFYICONDATA

'タスクトレイにアイコンを操作する
Private Declare Function Shell_NotifyIcon Lib "shell32.dll" Alias "Shell_NotifyIconA" (ByVal dwMessage As Long, lpData As NOTIFYICONDATA) As Long

Private Const NIM_ADD = &H0      'アイコン追加
Private Const NIM_MODIFY = &H1   'アイコン変更
Private Const NIM_DELETE = &H2   'アイコン削除

Private Const NIF_ICON = &H2     'アイコンを表示
Private Const NIF_MESSAGE = &H1  'マウスのメッセージを受け取る
Private Const NIF_TIP = &H4      'ヒントメッセージを表示

Private Const TRAY_TOOLTIP_TEXT As String = "タスクトレイ - サンプル"

Private Const WM_MOUSEMOVE = &H200
Private Const WM_LBUTTONDOWN = &H201
Private Const WM_RBUTTONDOWN = &H204

Private Const WM_USER = &H400
Private Const WM_TASKTRAY_ICON = WM_USER + &H200  '200は適当

Private Type RECT
    Left As Long
    Top As Long
    Right As Long
    Bottom As Long
End Type

Private Type POINTAPI
    X As Long
    Y As Long
End Type

'ポップアップメニューを表示する
Private Declare Function TrackPopupMenu Lib "User32" (ByVal hMenu As Long, ByVal wFlags As Long, ByVal X As Long, ByVal Y As Long, ByVal nReserved As Long, ByVal hWnd As Long, lprc As RECT) As Long

'メニューのハンドルを取得する
Private Declare Function GetMenu Lib "User32" (ByVal hWnd As Long) As Long

'サブメニューのハンドルを取得する
Private Declare Function GetSubMenu Lib "User32" (ByVal hMenu As Long, ByVal nPos As Long) As Long

'カーソルの位置を取得する
Private Declare Function GetCursorPos Lib "User32" (lpPoint As POINTAPI) As Long

'ウインドウサイズを取得する
Private Declare Function GetWindowRect Lib "User32" (ByVal hWnd As Long, lpRect As RECT) As Long

Private Const TPM_BOTTOMALIGN = &H20& '下
Private Const TPM_CENTERALIGN = &H4& '中央下
Private Const TPM_LEFTALIGN = &H0& '右下
Private Const TPM_RIGHTALIGN = &H8& '左下
Private Const TPM_LEFTBUTTON = &H0& '左クリック追跡
Private Const TPM_RIGHTBUTTON = &H2& '右クリック追跡

'指定ウインドウをアクティブにする
Private Declare Function SetForegroundWindow Lib "User32" (ByVal hWnd As Long) As Long

'前回のウインドウプロシージャ
Private 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 ret As Long

  If OldWindowhwnd <> 0 Then
      ret = SetWindowLong(hWnd, GWL_WNDPROC, OldWindowhwnd)
      OldWindowhwnd = 0
  End If
End Sub

'-----------------------------------------------------------------------
' 関数名 : SetIconToTasktray
' 機能   : タスクトレイにアイコンを設定する
' 引数   : (in) hWnd  … メインウインドウのハンドル
'           (in) hIcon … 表示するアイコンのハンドル
' 戻り値 : 正常:1 エラー:1以外
'-----------------------------------------------------------------------
Public Function SetIconToTasktray(ByVal hWnd As Long, ByVal hIcon As Long) As Long
    Dim FuncRet As Long
    With udtNotifyIcon
        .cbSize = Len(udtNotifyIcon)
        .hWnd = hWnd
        '.uID = 1
        .uFlags = NIF_ICON Or NIF_MESSAGE Or NIF_TIP
        .uCallbackMessage = WM_TASKTRAY_ICON
        .hIcon = hIcon
        .szTip = TRAY_TOOLTIP_TEXT & vbNullChar
    End With

    SetIconToTasktray = Shell_NotifyIcon(NIM_ADD, udtNotifyIcon)
End Function

'-----------------------------------------------------------------------
' 関数名 : DeleteTasktrayIcon
' 機能   : タスクトレイにアイコンを破棄する
' 戻り値 : 正常:1 エラー:1以外
'-----------------------------------------------------------------------
Public Function DeleteTasktrayIcon() As Long
    DeleteTasktrayIcon = Shell_NotifyIcon(NIM_DELETE, udtNotifyIcon)
End Function

'-----------------------------------------------------------------------
' 関数名 : WindowProc
' 機能   : いわずとしれたウインドウプロシージャ
'-----------------------------------------------------------------------
Public 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_TASKTRAY_ICON
          Select Case lParam
              Case WM_LBUTTONDOWN
                  '左から0番目のメニューを表示
                  Call ShowPopUpMenu(hWnd, 0)
              Case WM_RBUTTONDOWN
                  '左から1番目のメニューを表示
                  Call ShowPopUpMenu(hWnd, 1)
          End Select
    End Select

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

End Function


'-----------------------------------------------------------------------
' 関数名 : ShowPopUpMenu
' 機能   : マウスポインタ位置にメニューを表示する
' 引数   : (in) hWnd  … 表示するメニューを持つウインドウのハンドル
'          (in) MenuPos … 最上位メニュー位置(0-Base)
' 戻り値 : 正常:1  エラー:0
'-----------------------------------------------------------------------
Public Function ShowPopUpMenu(ByVal hWnd As Long, ByVal MenuPos As Long) As Long

    Dim udtPA As POINTAPI
    Dim udtRect As RECT
    Dim MenuhWnd As Long
    Dim SubMenuhWnd As Long

    'カーソルの位置を取得
    Call GetCursorPos(udtPA)

    'ウインドウ座標取得
    Call GetWindowRect(hWnd, udtRect)

    'メニューのハンドルを取得
    MenuhWnd = GetMenu(hWnd)

    'サブメニューのハンドルを取得
    SubMenuhWnd = GetSubMenu(MenuhWnd, MenuPos)

    'おまじない
    'こうすると、メニューが出ている状態でデスクトップをクリックしたら
    'メニューが消えてくれる
    Call SetForegroundWindow(hWnd)

    'ポップアップメニュー表示
    ShowPopUpMenu = TrackPopupMenu(SubMenuhWnd, TPM_CENTERALIGN, _
                                        udtPA.X, udtPA.Y, 0&, hWnd, udtRect)
End Function

あとはフォームに適当なメニューを作り、Load、UnLoad イベントで各処理を呼べばよい。

Private Sub Form_Load()
    Dim FuncRet As Long
    'タスクトレイにアイコンを設定
    FuncRet = SetIconToTasktray(Me.hWnd, Me.Icon)
    If FuncRet <> 1 Then
        Call MsgBox("タスクトレイにアイコンを設定できません", vbOKOnly + vbInformation)
        Unload Me
    End If
    
    Call SubClass(Me.hWnd)
End Sub

Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
    Dim FuncRet As Long
    'タスクトレイからアイコンを削除
    FuncRet = DeleteTasktrayIcon
    If FuncRet <> 1 Then
        Call MsgBox("タスクトレイからアイコンを破棄できません", vbOKOnly + vbInformation)
    End If

    Call UnSubClass(Me.hWnd)
End Sub

Private Sub mnuBeep_Click()
    Beep
End Sub

Private Sub mnuEnd_Click()
    Unload Me
End Sub

戻る