program ToTray;
{
  Copyright (c) 1996 by Charlie Calvert
  Modifications by Florian Klaempfl
  Standard Windows API application written in Object Pascal.
  No VCL code included. This is all done on the Windows API
  level.
  Last modifications by Torbins.
}
uses
  Messages, Windows;
const
  ClassName = 'ToTrayWindClass';
  WindowName = 'ToTray';
  NIM_ADD         = $00000000;
  NIM_MODIFY      = $00000001;
  NIM_DELETE      = $00000002;
  NIF_MESSAGE     = $00000001;
  NIF_ICON        = $00000002;
  NIF_TIP         = $00000004;
type
  TNotifyIconData = record
    cbSize: DWORD;
    Wnd: HWND;
    uID: UINT;
    uFlags: UINT;
    uCallbackMessage: UINT;
    hIcon: HICON;
    szTip: array [0..63] of Char;
  end;
  PNotifyIconData = ^TNotifyIconData;
  function Shell_NotifyIcon(dwMessage: DWORD; lpData: PNotifyIconData): BOOL;
    stdcall; external 'shell32.dll' name 'Shell_NotifyIconA';
var
  IconCapt: PChar;
  hWindow, hFWind: HWnd;
  hIcon: Windows.HICON;
  i: Integer;
  WndThread: Cardinal;
  TrayMes, TaskbarCreated: Cardinal;
  ThreadWindows: array of HWnd;
  AMessage: Msg;
  Param: String;
 { From SysUtils }
function StrComp(const Str1, Str2: PChar): Integer; assembler;
asm
        PUSH    EDI
        PUSH    ESI
        MOV     EDI,EDX
        MOV     ESI,EAX
        MOV     ECX,0FFFFFFFFH
        XOR     EAX,EAX
        REPNE   SCASB
        NOT     ECX
        MOV     EDI,EDX
        XOR     EDX,EDX
        REPE    CMPSB
        MOV     AL,[ESI-1]
        MOV     DL,[EDI-1]
        SUB     EAX,EDX
        POP     ESI
        POP     EDI
end;
function StrLCopy(Dest: PChar; const Source: PChar; MaxLen: Cardinal): PChar; assembler;
asm
        PUSH    EDI
        PUSH    ESI
        PUSH    EBX
        MOV     ESI,EAX
        MOV     EDI,EDX
        MOV     EBX,ECX
        XOR     AL,AL
        TEST    ECX,ECX
        JZ      @@1
        REPNE   SCASB
        JNE     @@1
        INC     ECX
@@1:    SUB     EBX,ECX
        MOV     EDI,ESI
        MOV     ESI,EDX
        MOV     EDX,EDI
        MOV     ECX,EBX
        SHR     ECX,2
        REP     MOVSD
        MOV     ECX,EBX
        AND     ECX,3
        REP     MOVSB
        STOSB
        MOV     EAX,EDX
        POP     EBX
        POP     ESI
        POP     EDI
end;
 { From JclSysInfo }
function GetWindowIcon(Wnd: HWND): Windows.HICON;
begin
  Result := GetClassLong(Wnd, GCL_HICON);
  if Result = 0 then
    Result := SendMessage(Wnd, WM_GETICON, ICON_BIG, 0);
  if Result = 0 then
    Result := LoadIcon(0, MakeIntResource(32512));
end;
 { Add Icon to Tray }
procedure AddIcon(h: HWND; m: Cardinal; hi: Windows.HICON; t: PChar);
var NIData: TNotifyIconData;
begin
  FillChar(NIData, SizeOf(TNotifyIconData), 0);
  with NIData do
  begin
    cbSize := SizeOf(TNotifyIconData);
    Wnd := h;
    uID := 1;
    uFlags := NIF_ICON or NIF_MESSAGE or NIF_TIP;
    uCallBackMessage := m;
    hIcon := hi;
    CopyMemory(@szTip, t, 64);
  end;
  Shell_NotifyIcon(NIM_ADD, @nidata);
end;
procedure ModifyIcon(h: HWND; f: Cardinal; hi: Windows.HICON; t: PChar);
var NIData: TNotifyIconData;
begin
  FillChar(NIData, SizeOf(TNotifyIconData), 0);
  with NIData do
  begin
    cbSize := SizeOf(TNotifyIconData);
    Wnd := h;
    uID := 1;
    uFlags := f;
    hIcon := hi;
    if f and NIF_TIP = NIF_TIP then
      CopyMemory(@szTip, t, 64);
  end;
  Shell_NotifyIcon(NIM_MODIFY, @nidata);
end;
 { Remove Icon from Tray }
procedure DelIcon(h: HWND);
var NIData: TNotifyIconData;
begin
  FillChar(NIData, SizeOf(TNotifyIconData), 0);
  with NIData do
  begin
    cbSize := SizeOf(TNotifyIconData);
    Wnd := h;
    uID := 1;
  end;
  Shell_NotifyIcon(NIM_DELETE, @nidata);
end;
 { Process Window and Tray Massages }
function WindowProc(Window: HWnd; AMessage: UINT; WParam : WPARAM;
                    LParam: LPARAM): LRESULT; stdcall; export;
var
  twl, j: Integer;
  tmpIcon: Windows.HICON;
  tmpCapt: PChar;
  f: Cardinal;
begin
  WindowProc := 0;
  case AMessage of
    wm_Destroy:
      begin
        DelIcon(hWindow);
        twl := Length(ThreadWindows);
        if twl > 0 then
          for j := 0 to twl-1 do
            ShowWindow(ThreadWindows[j], SW_SHOW)
        else
          ShowWindow(hFWind, SW_SHOW);
        PostQuitMessage(0);
        Exit;
      end;
    wm_Timer:
      begin
        GetMem(tmpCapt, 64);
        try
          FillChar(tmpCapt^, 63, 0);
          GetWindowText(hFWind, tmpCapt, 63);
          tmpIcon := GetWindowIcon(hFWind);
          f := 0;
          if tmpIcon <> hIcon then
          begin
            f := NIF_ICON;
            hIcon := tmpIcon;
          end;
          if StrComp(tmpCapt, IconCapt) <> 0 then
          begin
            f := f or NIF_TIP;
            StrLCopy(IconCapt, tmpCapt, 63);
          end;
          if f <> 0 then
            ModifyIcon(hWindow, f, hIcon, IconCapt);
        finally
          FreeMem(tmpCapt);
        end;
      end;
  else
    if AMessage = TrayMes then
    begin
      if (lParam = WM_LBUTTONDOWN) or (lParam = WM_RBUTTONDOWN) or
        (lParam = WM_MBUTTONDOWN) then
      begin
        PostMessage(hWindow, WM_DESTROY, 0, 0);
      end;
    end
    else
      if AMessage = TaskbarCreated then
      begin
        DelIcon(hWindow);
        FillChar(IconCapt^, 63, 0);
        GetWindowText(hFWind, IconCapt, 63);
        AddIcon(hWindow, TrayMes, GetWindowIcon(hFWind), IconCapt);
      end;
  end;
  WindowProc := DefWindowProc(Window, AMessage, WParam, LParam);
end;
 { Register the Window Class }
function WinRegister: Boolean;
var
  WindowClass: WndClass;
begin
  WindowClass.Style := 0;
  WindowClass.lpfnWndProc := @WindowProc;
  WindowClass.cbClsExtra := 0;
  WindowClass.cbWndExtra := 0;
  WindowClass.hInstance := system.MainInstance;
  WindowClass.hIcon := LoadIcon(0, idi_Application);
  WindowClass.hCursor := LoadCursor(0, idc_Arrow);
  WindowClass.hbrBackground := COLOR_WINDOW;
  WindowClass.lpszMenuName := nil;
  WindowClass.lpszClassName := ClassName;
  Result := RegisterClass(WindowClass) <> 0;
end;
 { Create the Window Class }
function WinCreate: HWnd;
begin
  Result := CreateWindow(ClassName, WindowName, ws_Overlapped, -100, -100, 50,
    30, 0, 0, system.MainInstance, nil);
  {if Result <> 0 then begin
    ShowWindow(Result, SW_SHOWNA);
    ShowWindow(Result, SW_HIDE);
  end;}
end;
function GetErrorStr(code:Cardinal): string;
var
  MsgBuf: PChar;
begin
  Result:='';
  if FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER or
       FORMAT_MESSAGE_FROM_SYSTEM, Nil, code, 0, @MsgBuf, 0, Nil)>0 then
  begin
    Result:=MsgBuf;
    LocalFree(Cardinal(MsgBuf));
  end;
end;
function OnTaskBar(Window: HWnd): Boolean;
begin
  Result := False;
  if (GetParent(Window) = 0) and (GetWindowLong(Window, GWL_EXSTYLE) and
    WS_EX_TOOLWINDOW = 0) and (GetWindowLong(Window, GWL_EXSTYLE) and
    WS_EX_NOACTIVATE = 0) then
    Result := True;
end;
function EnumThreadWndProc(Window: HWnd; LParam: LPARAM): BOOL; stdcall;
var
  twl: Integer;
  CName: PAnsiChar;
begin
  Result := True;
  if IsWindowVisible(Window) then
  begin
    twl := Length(ThreadWindows);
    SetLength(ThreadWindows, twl+1);
    ThreadWindows[twl] := Window;
    GetMem(CName, 256);
    try
      FillChar(CName^, 255, 0);
      if GetClassName(Window, CName, 255) = 0 then
        MessageBox(0, PChar('Can''t get Class Name'#13#10+
          GetErrorStr(GetLastError)), nil, mb_Ok);
      if (CName = 'TApplication') and OnTaskBar(Window) then
        hFWind := Window;
    finally
      FreeMem(CName);
    end;
  end;
end;
begin
  TrayMes := RegisterWindowMessage('WM_Tray');
  TaskbarCreated := RegisterWindowMessage('TaskbarCreated');
  hFWind := GetForegroundWindow;
  //hFWind := FindWindow('TTimerForm', nil);
  if (hFWind <> 0) and (hFWind <> GetDesktopWindow) and
    (hFWind <> FindWindow('Shell_TrayWnd', nil)) and
    (hFWind <> FindWindow('Progman', nil)) then
  begin
    if not WinRegister then
    begin
      MessageBox(0, PChar('Register failed'#13#10+GetErrorStr(GetLastError)),
        nil, mb_Ok);
      Exit;
    end;
    hWindow := WinCreate;
    if longint(hWindow) = 0 then
    begin
      MessageBox(0, PChar('WinCreate failed'#13#10+GetErrorStr(GetLastError)),
        nil, mb_Ok);
      Exit;
    end;
    GetMem(IconCapt, 64);
    try
      Param := ParamStr(1);
      if (Param = '/AllWnd') or (Param = '-AllWnd') then
      begin
        WndThread := GetWindowThreadProcessId(hFWind);
        EnumThreadWindows(WndThread, @EnumThreadWndProc, 0);
        FillChar(IconCapt^, 63, 0);
        GetWindowText(hFWind, IconCapt, 63);
        hIcon := GetWindowIcon(hFWind);
        AddIcon(hWindow, TrayMes, hIcon, IconCapt);
        if Length(ThreadWindows) > 0 then
          for i := 0 to Length(ThreadWindows)-1 do
            ShowWindow(ThreadWindows[i], SW_HIDE)
        else
          ShowWindow(hFWind, SW_HIDE);
      end
      else
      begin
        FillChar(IconCapt^, 63, 0);
        GetWindowText(hFWind, IconCapt, 63);
        hIcon := GetWindowIcon(hFWind);
        AddIcon(hWindow, TrayMes, hIcon, IconCapt);
        ShowWindow(hFWind, SW_HIDE);
      end;
      //Param := '';
      if SetTimer(hWindow, 1, 1000, nil) = 0 then
        MessageBox(0, PChar('SetTimer failed'#13#10+GetErrorStr(GetLastError)),
          nil, mb_Ok);
      while GetMessage(AMessage, 0, 0, 0) do
      begin
        TranslateMessage(AMessage);
        DispatchMessage(AMessage);
      end;
    finally
      FreeMem(IconCapt);
      KillTimer(hWindow, 1);
    end;
    Halt(AMessage.wParam);
  end;
end.