Найдите значок в трее - PullRequest
       50

Найдите значок в трее

1 голос
/ 29 февраля 2012

У меня проблема с поиском иконки в трее (в пикселях) на панели задач.

Я могу найти трей, но не иконку. Это код, который я использую:

unit uTrayIconPosition;

interface

uses
  Types;

function GetTrayIconPosition(const AWnd: THandle; const AButtonID: Integer; var APosition: TRect): Boolean;

implementation

uses
  Windows, CommCtrl, Classes, SysUtils;

function EnumWindowsFunc(AHandle: THandle; AList: TStringList): Boolean; stdcall;
var
  P: array [0..256] of Char;
  S: string;
begin
  if GetClassName(AHandle, P, SizeOf(P) - 1) <> 0 then
  begin
    S := P;
    if S = AList[0] then
    begin
      AList[0] := IntToStr(AHandle);
      Result := False;
    end
    else
      Result := True;
  end
  else
    Result := True;
end;

function FindClass(AName: string; AHandle: THandle; var AChild: THandle): Boolean;
var
  List: TStringList;
begin
  Result := False;
  try
    List := TStringList.Create;
    try
      List.Add(AName);
        EnumChildWindows(AHandle, @EnumWindowsFunc, LParam(List));
      if List.Count > 0 then
      begin
        AChild := StrToInt(List[0]);
        Result := True;
      end;
    finally
      List.Free;
    end;
  except
  end;
end;

// --- Ручка уведомления Wnd

function GetTrayNotifyWnd: THandle;
var
  ShellTray: THandle;
  TrayNotify: THandle;
  ToolBar: THandle;
begin
    Result := 0;
  ShellTray := FindWindow('Shell_TrayWnd', nil);
  if ShellTray <> 0 then
    if FindClass('TrayNotifyWnd', ShellTray, TrayNotify) then
      if IsWindow(TrayNotify) then
        if FindClass('ToolbarWindow32', TrayNotify, ToolBar) then
                Result := ToolBar;
end;

// --- Поиск прямоугольника лотка

function GetTrayWndRect: TRect;
var
  R: TRect;
  Handle: THandle;
  Width: Integer;
  Height: Integer;
begin
    Handle := GetTrayNotifyWnd;
    if Handle > 0 then
    begin
        GetWindowRect(Handle, R);
        Result := R;
    end
  else
  begin
      Width := GetSystemMetrics(SM_CXSCREEN);
    Height := GetSystemMetrics(SM_CYSCREEN);
    Result := Rect(Width - 40, Height - 20, Width, Height);
  end;
end;

// --- Основная функция, которая должна найти иконку в трее

function GetTrayIconPosition(const AWnd: THandle; const AButtonID: Integer; var APosition: TRect): Boolean;
var
  hWndTray: HWND;
  dwTrayProcessID: DWORD;
  hTrayProc: THandle;
  iButtonsCount: Integer;
  lpData: Pointer;
  bIconFound: Boolean;
  iButton: Integer;
  dwBytesRead: DWORD;
    ButtonData: TTBBUTTON;
  dwExtraData: array [0..1] of DWORD;
  hWndOfIconOwner: THandle;
  iIconId: Integer;
//  rcPosition: TPoint;
  rcPosition: TRect;
begin
    Result := False;

  hWndTray := GetTrayNotifyWnd;
  if hWndTray = 0 then
    Exit;

    dwTrayProcessID := 0;
    GetWindowThreadProcessId(hWndTray, dwTrayProcessID);
    if dwTrayProcessID <= 0 then
        Exit;

    hTrayProc := OpenProcess(PROCESS_ALL_ACCESS, False, dwTrayProcessID);
    if hTrayProc = 0 then
        Exit;

    iButtonsCount := SendMessage(hWndTray, TB_BUTTONCOUNT, 0, 0);
  lpData := VirtualAllocEx(hTrayProc, nil, SizeOf(TTBBUTTON), MEM_COMMIT, PAGE_READWRITE);
    if (lpData = nil) or (iButtonsCount < 1) then
    begin
        CloseHandle(hTrayProc);
        Exit;
    end;

    bIconFound := False;
    for iButton :=0 to  iButtonsCount - 1 do
    begin
        dwBytesRead := 0;
        SendMessage(hWndTray, TB_GETBUTTON, iButton, LPARAM(lpData));
        ReadProcessMemory(hTrayProc, lpData, @ButtonData, SizeOf(TTBBUTTON), dwBytesRead);
        if dwBytesRead < SizeOf(TTBBUTTON) then
            Break;

    dwExtraData[0] := 0;
    dwExtraData[1] := 0;
        ReadProcessMemory(hTrayProc, Pointer(ButtonData.dwData), @dwExtraData, SizeOf(dwExtraData), dwBytesRead);
        if dwBytesRead < SizeOf(dwExtraData) then
            Break;

        hWndOfIconOwner := THandle(dwExtraData[0]);
        iIconId := Integer(dwExtraData[1]);
        if hWndOfIconOwner = AWnd then
        if iIconId = AButtonID then
            begin
            if (ButtonData.fsState or TBSTATE_HIDDEN) = 1 then
                Break;

        SendMessage(hWndTray, TB_GETITEMRECT, iButton, LPARAM(lpData));
        ReadProcessMemory(hTrayProc, lpData, @rcPosition, SizeOf(TREct), dwBytesRead);
        if dwBytesRead < SizeOf(TRect) then
          Break;

        MapWindowPoints(hWndTray, 0, rcPosition, 2);
        APosition := rcPosition;
        bIconFound := True;
        Break;
      end;
    end;

    if not bIconFound then
        APosition := GetTrayWndRect;
    VirtualFreeEx(hTrayProc, lpData, 0, MEM_RELEASE);
    CloseHandle(hTrayProc);
    Result := True;
end;

end.

Алгоритм обнаруживает # значков в трее, но не отображает каждую из них.

Это добавлено:

Потому что это решение работает только в XP и 32-битных системах, я попробовал следующее:

{$EXTERNALSYM Shell_NotifyIconGetRect}
function Shell_NotifyIconGetRect(const _in: NOTIFYICONIDENTIFIER; var _out: TRECT): HRESULT; stdcall;

implementation

function Shell_NotifyIconGetRect; external 'Shell32.dll' name 'Shell_NotifyIconGetRect';

Delphi 2007 не имеет этой функции, а также этой структуры:

type
  NOTIFYICONIDENTIFIER = record
    cbSize  : DWORD;
    hWnd    : HWND;
    uID     : UINT;
    guidItem: TGUID;
end;
  PNOTIFYICONIDENTIFIER = ^NOTIFYICONIDENTIFIER;

После того как я создал иконку в трее с помощью Shell_NotifyIcon, я попытался передать эту структуру _NOTIFYICONDATA hWND этой новой структуре NOTIFYICONIDENTIFIER>

var
  R: TRect;
  S: NOTIFYICONIDENTIFIER;

FillChar(S, SizeOf(S), #0);
S.cbSize := SizeOf(NOTIFYICONIDENTIFIER);
S.hWnd := ATrayIcon.Data.Wnd;
S.uID := ATrayIcon.Data.uID;

Result := Shell_NotifyIconGetRect(S, R) = S_OK;

Это работает правильно, и я получаю в структуре Rect левый верхний угол моей иконки в трее.

1 Ответ

5 голосов
/ 29 февраля 2012

В Windows 7 и выше вы должны использовать функцию API, которую MS представила для этой цели: Shell_NotifyIconGetRect.

Ваш текущий код не работает по одной или нескольким из следующих причин:

  1. Вы пытаетесь прочитать 32-битные версии структур из 64-битного процесса. В этом случае TTBBUTTON имеет другую компоновку и размер менее 64 бит, и процесс, который вы атакуете, представляет собой 64-битный проводник.
  2. Реализация (подробности, на которую вы полагаетесь) области уведомлений изменилась между XP и 7. Я не знаю, правда ли это, но это может быть!
...