タスクトレイに登録したアイコンの位置を知るには?

解決


ABC  2005-12-07 08:05:03  No: 128637

VB6.0にて常駐アプリを作成しているのですが、

Shell_NotifyIconAにてタスクトレイに登録した自アイコンの位置を知る方法はありますか?

ホットキーを押された時にメニューを表示したいのですが、タスクトレイのアイコンの位置に合わせたいと思っています。

よろしくお願いします。


かぶ  2005-12-07 09:47:02  No: 128638

タスクトレイのアイコン表示領域はツールバーと同じクラスなので
取得できるとしてもアイコンが並んでいる矩形領域しか取得できない
のではないでしょうか。(時計の表示領域はTrayClockWClassとかいう
別ウィンドウになっているみたいですね)

よって、アイコンのサイズと何番目に表示されているかが分かれば
おおよその位置は計算できると思いますが。

詳しいことは調べてないのでなんともいえません。


かぶ  2005-12-07 10:01:47  No: 128639

ちょと調べたところTB_GETRECTメッセージを送れば
ボタンの四隅がわかるみたいです。

参考URL
http://www.kumei.ne.jp/c_lang/sdk2/sdk_189.htm


ABC  2005-12-08 03:05:35  No: 128640

>かぶさん
ありがとうございます。

TB_GETRECTについて調べてみたのですが、
TB_GETRECT=&H433でよいのでしょうか?

Dim typBtnRect  As RECT
SendMessage(Me.hwnd,TB_GETRECT,?,typBtnRect)

第三引数に何を入れたらよいか分かりません。
0を入れて実行したら、typBtnRectには全て0が入っていました。

また、アイコンが何番目に表示されているのかというのは取得可能なのでしょうか?

よろしくお願いします。


かぶ  2005-12-08 07:11:00  No: 128641

いろいろ調べるうちに結構、奥が深いような予感がしてきました。
TB_GETRECTの値はそれでいいです。
しかしながら、これをそのまま実行しても情報はとれないようで

http://forums.belution.com/ja/vc/000/271/38.shtml

上記URLにタスクトレイのツールバーの情報にアクセスする方法がありますが
これを参考にしてみてはいかがでしょう。

ちなみに
SendMessageのウィンドウハンドルはフォームのウィンドウハンドルじゃなく
タスクトレイのツールバーのウィンドウハンドルです
タスクトレイのツールバーというのは、ここを参考にしてみて下さい。
http://homepage1.nifty.com/kazubon/progdoc/tclock/taskbarhistory.html

第3引数はCommandIDです。
詳しくはこのあたりをご覧下さい。
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/commctls/toolbar/messages/tb_getrect.asp

で、自分のアイコンを識別する方法ですが、アイコン作成時に何か識別
できるボタンテキストを仕込んでおいてGETBUTTONTEXT等で区別するとか
でしょうか。他にもあるかもしれません。


ABC  2005-12-09 01:33:32  No: 128642

>かぶさん
ありがとうございます。

タスクバーのツールバー(?)のハンドルは以下のようにして取得することが出来ました。

Dim hwndTaskbar As Long
Dim hwndButton  As Long
Dim hwndTray    As Long

            'タスクバーのハンドルを取得
            hwndTaskbar = FindWindowEx(0&, 0&, "Shell_TrayWnd", vbNullString)
            'トレイのハンドルを取得
            hwndButton = FindWindowEx(hwndTaskbar, 0&, "TrayNotifyWnd", vbNullString)
            'windows xp の場合、必要(トレイのアイコンのハンドルを取得)
            If XPかどうかフラグ = True Then hwndButton = FindWindowEx(hwndButton, 0&, "SysPager", vbNullString)
            'トレイのアイコンのハンドルを取得
            hwndTray = FindWindowEx(hwndButton, 0&, "ToolbarWindow32", vbNullString)

ここから、トレイに登録されているアイコンボタンの情報を取得しようと
以下のような処理を書きました。

Public Const TB_GETBUTTON = WM_USER + 23
Public Const TB_BUTTONCOUNT = WM_USER + 24

Dim intButtonCount  As Integer
Dim tbb     As TBBUTTON

            '/*    ボタンの数を取得    */
            intButtonCount = SendMessage(hwndTray, TB_BUTTONCOUNT, 0, 0)
            For i = 0 To intButtonCount - 1
               '/*    i 番目のボタンの情報を取得    */
                Call SendMessage(hwndTray, TB_GETBUTTON, i, tbb)
            Next i

しかし、tbbに値が入ってきません。
どのようにしたら、ボタンの情報が取得できるのでしょうか?

よろしくお願いします。


かぶ  2005-12-09 06:18:54  No: 128643

前レスにも書いたようにそのままTB_GETRECTやTB_GETBUTTONを行っても
別プロセスで確保されたメモリにはアクセスできないようです。
http://forums.belution.com/ja/vc/000/271/38.shtml
前レスのアクセス方法のリンク先をVB流に書き直すと以下のようになるでしょうか。
このサンプルではタスクトレイの全てのボタンの情報を取得して、その中で可視状態
にあるボタンの四隅を取得し、最後にスクリーン座標でDebug.Printしています。

  Dim hWnd        As Long
  Dim iCnt        As Long
  Dim i           As Long
  Dim lngRet      As Long
  Dim lngPtr      As Long
  Dim lngPID      As Long
  Dim hProc       As Long
  Dim oBtn()      As TBBUTTON
  Dim lngRdCnt    As Long
  Dim rctIcon()   As RECT
  Dim rctWin      As RECT   'ツールバーのWindow Rect
  Dim rctClient   As RECT   'ツールバーのClient Rect

  'タスクトレイのツールバー領域のウィンドウハンドルを取得(XP)の場合
  hWnd = FindWindowEx( _
         FindWindowEx( _
         FindWindowEx( _
         FindWindowEx(0&, 0&, "Shell_TrayWnd", ByVal 0) _
         , 0&, "TrayNotifyWnd", ByVal 0) _
         , 0&, "SysPager", ByVal 0) _
         , 0&, "ToolbarWindow32", ByVal 0)

  'ツールバーを作成したプロセスでメモリを確保
  GetWindowThreadProcessId hWnd, lngPID
  hProc = OpenProcess(PROCESS_ALL_ACCESS, 0, lngPID)
  lngPtr = VirtualAllocEx(hProc, ByVal 0&, 65536, MEM_COMMIT, PAGE_READWRITE)
  
  'ボタンの数を取得
  iCnt = SendMessage(hWnd, TB_BUTTONCOUNT, 0&, 0&)

  'ツールバーのウィンドウ領域とクライアント領域を取得
  GetWindowRect hWnd, rctWin
  GetClientRect hWnd, rctClient

  Debug.Print "Count=" & Format(iCnt)
  ReDim oBtn(iCnt - 1)      'ボタン情報を取得する領域を確保
  ReDim rctIcon(iCnt - 1)   'ボタンの位置を取得する領域を確保
  
  For i = 0 To iCnt - 1
    'ボタン情報の取得
    lngRet = SendMessage(hWnd, TB_GETBUTTON, i, ByVal lngPtr)
    ReadProcessMemory hProc, ByVal lngPtr, oBtn(i), Len(oBtn(i)), lngRdCnt
    Debug.Print "Index=" & Format(i) & " idCommand=" & Format(oBtn(i).idCommand) & " Sate=" & Hex$(oBtn(i).fsState)
    If (oBtn(i).fsState And TBSTATE_HIDDEN) = 0 Then
      'ボタンが可視状態なら位置を取得
      lngRet = SendMessage(hWnd, TB_GETRECT, oBtn(i).idCommand, ByVal lngPtr)
      ReadProcessMemory hProc, ByVal lngPtr, rctIcon(i), Len(rctIcon(i)), lngRdCnt
      'スクリーン座標に変換して出力
      Debug.Print rctWin.Left + rctClient.Left + rctIcon(i).Left, rctWin.Top + rctClient.Top + rctIcon(i).Top, _
        rctWin.Left + rctClient.Left + rctIcon(i).Right, rctWin.Top + rctClient.Top + rctIcon(i).Bottom
    End If
  Next

  '後処理
  VirtualFreeEx hProc, ByVal lngPtr, 0, MEM_RELEASE
  CloseHandle hProc


ABC  2005-12-09 07:24:41  No: 128644

>かぶさん
ありがとうございます。

私も少し前に、そのことに気がつき表示されているアイコンの各座標を取得するところまで出来ました。

TBBUTTONのiStringってどこの文字列ポインタが入っているのでしょうか?
自アプリを検索するには使えませんか?

よろしくお願いします。


かぶ  2005-12-09 08:13:57  No: 128645

>TBBUTTONのiStringってどこの文字列ポインタが入っているのでしょうか?
>自アプリを検索するには使えませんか?

前レスのコードにiStringの取得部分を追加し実行させたところ(下記コード参照)
どうもToolTipテキストの文字のような気がしますね。
他のアイコンのテキストとかぶらないものを設定しておけば
検索に使えるかもしれません。

  For i = 0 To iCnt - 1
    'ボタン情報の取得
    lngRet = SendMessage(hWnd, TB_GETBUTTON, i, ByVal lngPtr)
    ReadProcessMemory hProc, ByVal lngPtr, oBtn(i), Len(oBtn(i)), lngRdCnt
    Debug.Print "Index=" & Format(i) & " idCommand=" & Format(oBtn(i).idCommand) & " Sate=" & Hex$(oBtn(i).fsState) & " iString=" & Hex$(oBtn(i).iString)
    If (oBtn(i).fsState And TBSTATE_HIDDEN) = 0 Then
      'ボタンが可視状態なら位置を取得
      lngRet = SendMessage(hWnd, TB_GETRECT, oBtn(i).idCommand, ByVal lngPtr)
      ReadProcessMemory hProc, ByVal lngPtr, rctIcon(i), Len(rctIcon(i)), lngRdCnt
      'スクリーン座標に変換して出力
      Debug.Print rctWin.Left + rctClient.Left + rctIcon(i).Left, rctWin.Top + rctClient.Top + rctIcon(i).Top, _
        rctWin.Left + rctClient.Left + rctIcon(i).Right, rctWin.Top + rctClient.Top + rctIcon(i).Bottom
      
      '-------------------------------------------------------------追加分
      If oBtn(i).iString And &HFFFF0000 Then
        '上位ワードが0で無い場合文字列のポインタが入っている
        lngSize = SendMessage(hWnd, TB_GETBUTTONTEXTW, oBtn(i).idCommand, ByVal 0)  'テキストのサイズを取得
        If lngSize > 0 Then
          'テキストサイズはUnicodeの文字数で返ってくるみたいなので文字数×2バイト分の領域を確保
          ReDim bytText(lngSize * 2 - 1)
          ReadProcessMemory hProc, ByVal oBtn(i).iString, bytText(0), lngSize * 2, lngRdCnt
          '文字列の表示
          Debug.Print bytText
        End If
      End If
      '-------------------------------------------------------------追加分  ここまで
    End If
  Next


ABC  2005-12-10 08:56:58  No: 128646

>かぶさん
最後まで有難うございました。

とりあえず、ツールチップで判別し正しい座標を取得することができました。

サンプルソースもとても役に立ちました。

また、何かありましたらよろしくお願いします。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加