Minimalizowanie do tray innej aplikacji

0

Witam,
czy jest możliwość zminimalizowania do traya programem delphi innej, zewnętrznej aplikacji która nie ma możliwości minimalizowania do traya?
Jeżeli tak to proszę o wskazówki :)

0
SendMessage(wnd, WM_SYSCOMMAND, SC_MINIMIZE, 0);
0

OK, rozumiem,
ale jak minimalizowac i umieszczac ikone w tray?

a ta zmienna NotifRec
to co to jest?

1

2 parametr funkcji Shell_NotifyIcon to wskaźnik na rekord który zawiera między innymi uchwyt okna które chcesz zminimalizować (uwaga leniwy przykład zero obsługi komunikatów i usuwania ikony z traya)

uses
  shellapi;

procedure TForm1.Button1Click(Sender: TObject);
const
  WINDOW_TITLE = 'Kalkulator';
var
  hWnd: THandle;
  TrayRec: TNotifyIconData;
begin
  hWnd:= FindWindow(nil, WINDOW_TITLE);
  ZeroMemory(@TrayRec, SizeOf(TrayRec));
  TrayRec.cbSize:= SizeOf(TrayRec);
  TrayRec.Wnd:= hWnd;
  TrayRec.uFlags:= NIF_ICON;
  TrayRec.hIcon:= Application.Icon.Handle;
  if Shell_NotifyIcon(NIM_ADD, @TrayRec) then
    SendMessage(hWnd, WM_SYSCOMMAND, SC_MINIMIZE, 0);
end;
0

dzięki, to wystarczy :)

2

Pozwoiliłem sobie zatwierdzić odpowiedź @kAzek'a jako rozwiązanie. Jednak nie jest ono według mnie kompletne. Ponieważ między innymi będzie problem z ikonką i z obsługą menu. Nie da się niestety łatwo wedlug mojej wiedzy dostać do popup menu będącego pod TrayIconami obcych programów, do których kodu źródlowego nie mamy dostępu. Można wykombinować tak jak pokazuje w kodzie poniżej. Rozwiązanie tylko dla nazw plików Ansi. Widoczna będzie ikonka obcej aplikacji. Dwuklikiem na ikonkę przywrócimy aplikację. Przy zamykaniu aplikacji wywołującej aplikacja zostanie przywrócona.

Ponieważ sposób, ktory zaprezentował swoim kodem @kAzek może być wraz z dodaniem obslugi ikonki przeze mnie. Z tym, że niestety nadal pozostaje kwestia obsługi ikonki. Wiadomo, że nie "podczepimy" się pod obcy proces bez injekcji / wrappera dll. A ustawiając pole rekordu danych ikonki Wnd na Handle tego znalezionego okna, nie obsłużymy komunikatów. Natomiast ustawiając na Handle naszego okna ikonka zniknie nam po zamknięciu apki. Ot taki minus. Toteż ja w swoim pluginie dla WinAMP'a, który trochę inaczej realizuje minimalizowanie do TrayIkonki, symulowałem trzymanie Shift'a z małym Sleep'em. Ponieważ nie wyodrębniłem w żaden sposób parametrów dla komunikatu WM_COMMAND lub podobnego - do wysłania oknu głownemu aby to schowało się do TrayIkonki. Nie uzyskałem też podpowiedzi na ich forum supportu, wiec tak zostało.

Na szczęście pluginem można założyć hook na WH_CBT i uzyskać możliwość reakcji zanim zostanie dokonana minimalizacja. Jeżeli docelowy program, z którym tak chce kombinować przez minimalizacje do TrayIkonki posiada taką możłiwość i można go z jakimś combo zmimimalizować do traya z poprawnym menu i reagowaniem, to ja bym tak kombinował. Ewentualnie zawsze jeżeli bardzo chcesz udoskonalić jakiś program, żeby mógł się chować do ikonki w trayu, a nie do paska. To trzeba mozolnie napisać wrapper na którąś z systemowych dllek dla tego programu. W niej skorzystać z rzeczonego Hooka na okno programu i dorobić własną trayiconkę z reakcją na klawisze oraz menu.

Zaraz @babubabu mi zarzuci, że ciągle ostatnio musi czytać o technice wrappowania dllek, ale to na prawdę potężna możliowść. Może machne o tym artykuł jak znajdę trochę czasu i będzie zainteresowanie. A korzystająć z faktu, żę większość dllek systemowych i w ogółe przez LoadLibrary czy statycznie najpierw jest wyszukiwana z katalogu programu, to można taką dllkę z własnym kodem tam wrzucić, a do oryginalnych funkcji jakie należy exportować można się odwołać do systemowej dllki. Takie kombinacje przydają się nie tylko w starszych grach żeby dobrze się wyświetlały pod Windowsami od Visty wzwyż. Można również zrobić proste poprawki i próbowac udoskonalić zachowanie programu, jeżeli nie wszystkie jego zachowania nam odpowiadają. Tyle. Sorry za rozpisanie się ;)

//...
uses
  ShellApi, PSApi;

const
  Window_Title = 'Kalkulator';
  WM_TOOLTRAYICON = WM_USER + 1;

var
  DestH : THandle;
  OldWindowProc : Pointer;
  IconData : TNotifyIconData;

function GetIconFromHandle(WindowHandle : HWND) : HICON;

  function ProcessFullPath(Pid : DWORD) : string;
  var
    AHandle : THandle;
  begin
    Result := '';
    AHandle := OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ, False, Pid);
    if AHandle <> 0 then
    begin
      try
        SetLength(Result, MAX_PATH);
        if GetModuleFileNameEx(AHandle, 0, PChar(Result), MAX_PATH) > 0 then
        begin
          SetLength(Result, StrLen(PChar(Result)));
        end
        else
        begin
          Result := '';
        end;
      finally
        CloseHandle(AHandle);
      end;
    end;
  end;

  function ProcessFullPath64Bit(Pid : DWORD) : string;
  const
    PROCESS_QUERY_LIMITED_INFORMATION = $1000;
  var
    Len : DWord;
    AHandle, DllHandle : THandle;
    QueryFullProcessImageNameA : function(HProcess : THandle; dwFlags : DWord; lpExeName : PAnsiChar; lpdwSize : PDWord) : Bool; stdcall;
  begin
    Result := '';
    DllHandle := LoadLibrary('kernel32.dll');
    if DllHandle > 0 then
    begin
      QueryFullProcessImageNameA := GetProcAddress(DllHandle, 'QueryFullProcessImageNameA');
      if Assigned(QueryFullProcessImageNameA) then
      begin
        AHandle := OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, False, Pid);
        if AHandle <> 0 then
        begin
          Len := MAX_PATH;
          SetLength(Result, Len - 1);
          QueryFullProcessImageNameA(AHandle, 0, PAnsiChar(Result), @len);
          SetLength(Result, Len);
          CloseHandle(AHandle);
        end;
      end;
      FreeLibrary(DllHandle);
    end;
  end;

var
  Pid : DWORD;
  ProcesPath : string;
  FileInfo : SHFILEINFO;
begin
  Result := 0;
  if ISWindow(WindowHandle) then
  begin
    GetWindowThreadProcessId(WindowHandle, Pid);
    ProcesPath := ProcessFullPath(Pid);
    if ProcesPath = '' then
    begin
      ProcesPath := ProcessFullPath64Bit(Pid)
    end;
    SHGetFileInfo(PChar(ProcesPath), 0, FileInfo, SizeOf(FileInfo), SHGFI_ICON or SHGFI_LARGEICON);
    Result := FileInfo.hIcon;
  end;
end;

procedure RestoreApp;
begin
  if (IsWindow(DestH)) and (ISIconic(DestH)) then
  begin
    ShowWindow(DestH, SW_SHOW);
    ShowWindow(DestH, SW_RESTORE);
    Shell_NotifyIcon(NIM_DELETE, @IconData);
  end;
end;

function NewWindowProc(WindowHandle : hWnd; TheMessage : Longint; ParamW : Longint; ParamL : Longint) : Longint stdcall;
begin
  case TheMessage of
    WM_TOOLTRAYICON :
      begin
        case ParamL of
          WM_LBUTTONDBLCLK :
            begin
              RestoreApp;
            end;
        end;
        Result := 0;
        Exit;
      end;
    WM_DESTROY :
      begin
        RestoreApp;
      end;
  end;
  Result := CallWindowProc(OldWindowProc, WindowHandle, TheMessage, ParamW, ParamL);
end;

procedure TForm1.Button1Click(Sender : TObject);
begin
  DestH := FindWindow(nil, Window_Title);
  if DestH > 0 then
  begin
    if OldWindowProc = nil then
    begin
      OldWindowProc := Pointer(SetWindowLong(Self.Handle, GWL_WNDPROC, Longint(@NewWindowProc)));
    end;
    ZeroMemory(@IconData, SizeOf(IconData));
    with IconData do
    begin
      cbSize := SizeOf(IconData);
      Wnd := Self.Handle;
      uFlags := NIF_MESSAGE + NIF_ICON;
      hIcon := GetIconFromHandle(DestH);
      uCallbackMessage := WM_TOOLTRAYICON;
    end;
    if Shell_NotifyIcon(NIM_ADD, @IconData) then
    begin
      SendMessage(DestH, WM_SYSCOMMAND, SC_MINIMIZE, 0);
      ShowWindow(DestH, SW_HIDE);
    end;
  end;
end;

1 użytkowników online, w tym zalogowanych: 0, gości: 1