title: Locate Tray Icon
body: <p>I'm having problem locating tray icon (in px) on traybar.</p>
  
  <p>I can locate tray but not icon as well. This is the code I'm using:</p>
  
  <pre><code>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) &lt;&gt; 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 &gt; 0 then
        begin
          AChild := StrToInt(List[0]);
          Result := True;
        end;
      finally
        List.Free;
      end;
    except
    end;
  end;
  </code></pre>
  
  <p>// --- Handle of notify Wnd</p>
  
  <pre><code>function GetTrayNotifyWnd: THandle;
  var
    ShellTray: THandle;
    TrayNotify: THandle;
    ToolBar: THandle;
  begin
      Result := 0;
    ShellTray := FindWindow('Shell_TrayWnd', nil);
    if ShellTray &lt;&gt; 0 then
      if FindClass('TrayNotifyWnd', ShellTray, TrayNotify) then
        if IsWindow(TrayNotify) then
          if FindClass('ToolbarWindow32', TrayNotify, ToolBar) then
                  Result := ToolBar;
  end;
  </code></pre>
  
  <p>// --- Finding Tray rect</p>
  
  <pre><code>function GetTrayWndRect: TRect;
  var
    R: TRect;
    Handle: THandle;
    Width: Integer;
    Height: Integer;
  begin
      Handle := GetTrayNotifyWnd;
      if Handle &gt; 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;
  </code></pre>
  
  <p>// --- Main function that should locate tray icon</p>
  
  <pre><code>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 &lt;= 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 &lt; 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 &lt; SizeOf(TTBBUTTON) then
              Break;
  
      dwExtraData[0] := 0;
      dwExtraData[1] := 0;
          ReadProcessMemory(hTrayProc, Pointer(ButtonData.dwData), @dwExtraData, SizeOf(dwExtraData), dwBytesRead);
          if dwBytesRead &lt; 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 &lt; 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.
  </code></pre>
  
  <p>Algo detect # of Tray icons, but doesn't map each of them.</p>
  
  <h2>This is added:</h2>
  
  <p>Cause this solution works only under XP and 32bit systems I've tried following:</p>
  
  <pre><code>{$EXTERNALSYM Shell_NotifyIconGetRect}
  function Shell_NotifyIconGetRect(const _in: NOTIFYICONIDENTIFIER; var _out: TRECT): HRESULT; stdcall;
  
  implementation
  
  function Shell_NotifyIconGetRect; external 'Shell32.dll' name 'Shell_NotifyIconGetRect';
  </code></pre>
  
  <p>Delphi 2007 doesn't have this function mapped and also this structure:</p>
  
  <pre><code>type
    NOTIFYICONIDENTIFIER = record
      cbSize  : DWORD;
      hWnd    : HWND;
      uID     : UINT;
      guidItem: TGUID;
  end;
    PNOTIFYICONIDENTIFIER = ^NOTIFYICONIDENTIFIER;
  </code></pre>
  
  <p>After I've created my tray icon with Shell_NotifyIcon I've tried to pass that _NOTIFYICONDATA structure hWND to this new NOTIFYICONIDENTIFIER structure ></p>
  
  <pre><code>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;
  </code></pre>
  
  <p>This is working correctly and I receive in Rect structure upper left corner of my Tray Icon.</p>