web-dev-qa-db-fra.com

Comment porter ma candidature au premier plan?

Je connais toutes les raisons pour lesquelles c'est une mauvaise idée. Je n'aime pas qu'une application vole le focus d'entrée, mais c'est pour un usage purement personnel et je veux que cela se produise; cela ne dérangera rien.

(pour les curieux: j'exécute des tests unitaires dans NetBeans, qui génèrent un fichier journal. Lorsque mon application d'arrière-plan voit le changement d'horodatage du fichier journal, je veux qu'elle analyse le fichier journal et vienne à l'avant pour afficher les résultats).

Cette question n'a pas aidé, ni googlé. Il semble que BringToFront() n'ait pas fonctionné depuis longtemps et je ne trouve aucune alternative qui fonctionne.

Des idées?

Vous ne pouvez pas le faire de manière fiable. Windows XP avait une solution de contournement qui le permettrait, mais les versions depuis l'interdisent pour la plupart (voir la note ci-dessous). Ceci est expressément indiqué dans la section des remarques de la documentation MSDN sur - SetForegroundWindow :

Le système limite les processus qui peuvent définir la fenêtre de premier plan. Un processus ne peut définir la fenêtre de premier plan que si l'une des conditions suivantes est remplie:

  • Le processus est le processus de premier plan.
  • Le processus a été lancé par le processus de premier plan.
  • Le processus a reçu le dernier événement d'entrée.
  • Il n'y a pas de processus de premier plan.
  • Le processus de premier plan est en cours de débogage.
  • Le premier plan n'est pas verrouillé (voir LockSetForegroundWindow).
  • Le délai d'expiration du verrouillage de premier plan a expiré (voir SPI_GETFOREGROUNDLOCKTIMEOUT dans SystemParametersInfo).
  • Aucun menu n'est actif.

Une application ne peut pas forcer une fenêtre au premier plan pendant que l'utilisateur travaille avec une autre fenêtre. Au lieu de cela, Windows fait clignoter le bouton de la barre des tâches de la fenêtre pour informer l'utilisateur.

Notez le dernier paragraphe de la documentation citée, en particulier la première phrase.

Le problème avec

c'est pour un usage purement personnel et je veux que cela se produise; cela ne dérangera rien.

c'est que n'importe quelle application pourrait essayer de faire la même chose. Comment une "utilisation purement personnelle" vous indique-t-elle que vous êtes personnellement et pas n'importe quelle application? :-)

Remarque: J'ai essayé tout ce à quoi je peux penser pour que la documentation de l'EDI s'affiche au premier plan lorsque je clique sur le bouton d'aide, mais tout ce que je peux obtenir est le bouton de la barre des tâches clignotant (et moi, en tant qu'utilisateur, je le souhaite) .

17
Ken White

Voici quelque chose de simple qui semble fonctionner, testé avec plusieurs boîtes comprenant XP, Server2003, Vista, Server2008, W7. L'application de test a fonctionné avec un compte standard (ou administrateur), a volé le focus d'entrée du bloc-notes lors de l'écriture au premier plan.

var
  Input: TInput;
begin
  ZeroMemory(@Input, SizeOf(Input));
  SendInput(1, Input, SizeOf(Input)); // don't send anyting actually to another app..
  SetForegroundWindow(Handle);

Vous pouvez le modifier davantage f.i. pour une application réduite ou telle si nécessaire.

32
Sertac Akyuz

Je fais quelque chose de similaire dans l'une de mes applications et cette fonction fonctionne pour moi sous xp/Vista/w7:

function ForceForegroundWindow(hwnd: THandle): Boolean;
const
  SPI_GETFOREGROUNDLOCKTIMEOUT = $2000;
  SPI_SETFOREGROUNDLOCKTIMEOUT = $2001;
var
  ForegroundThreadID: DWORD;
  ThisThreadID: DWORD;
  timeout: DWORD;
begin
  if IsIconic(hwnd) then ShowWindow(hwnd, SW_RESTORE);

  if GetForegroundWindow = hwnd then Result := True
  else
  begin
    // Windows 98/2000 doesn't want to foreground a window when some other
    // window has keyboard focus

    if ((Win32Platform = VER_PLATFORM_WIN32_NT) and (Win32MajorVersion > 4)) or
      ((Win32Platform = VER_PLATFORM_WIN32_WINDOWS) and
      ((Win32MajorVersion > 4) or ((Win32MajorVersion = 4) and
      (Win32MinorVersion > 0)))) then
    begin
      Result := False;
      ForegroundThreadID := GetWindowThreadProcessID(GetForegroundWindow, nil);
      ThisThreadID := GetWindowThreadPRocessId(hwnd, nil);
      if AttachThreadInput(ThisThreadID, ForegroundThreadID, True) then
      begin
        BringWindowToTop(hwnd); // IE 5.5 related hack
        SetForegroundWindow(hwnd);
        AttachThreadInput(ThisThreadID, ForegroundThreadID, False);
        Result := (GetForegroundWindow = hwnd);
      end;
      if not Result then
      begin
        // Code by Daniel P. Stasinski
        SystemParametersInfo(SPI_GETFOREGROUNDLOCKTIMEOUT, 0, @timeout, 0);
        SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, TObject(0),
          SPIF_SENDCHANGE);
        BringWindowToTop(hwnd); // IE 5.5 related hack
        SetForegroundWindow(hWnd);
        SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, TObject(timeout), SPIF_SENDCHANGE);
      end;
    end
    else
    begin
      BringWindowToTop(hwnd); // IE 5.5 related hack
      SetForegroundWindow(hwnd);
    end;

    Result := (GetForegroundWindow = hwnd);
  end;
end;

Une autre solution consiste à ne pas voler le focus et à simplement placer la fenêtre TOPMOST dans le tampon z:

procedure ForceForegroundNoActivate(hWnd : THandle);

begin
 if IsIconic(Application.Handle) then
  ShowWindow(Application.Handle, SW_SHOWNOACTIVATE);
 SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE or SWP_NOACTIVATE or SWP_NOMOVE);
 SetWindowPos(hWnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE or SWP_NOACTIVATE or SWP_NOMOVE);
end;
12
whosrdaddy

Cela devrait fonctionner même sous Windows7 + 8 OS. La seule exigence est qu'une application elle-même puisse appeler des fonctions. Il est plus difficile d'utiliser une application externe pour définir la fenêtre d'un autre processus.

Application.Restore; // unminimize window, makes no harm always call it
SetWindowPos(self.Handle, HWND_NOTOPMOST,0,0,0,0, SWP_NOMOVE or SWP_NOSIZE);
SetWindowPos(self.Handle, HWND_TOPMOST,0,0,0,0, SWP_NOMOVE or SWP_NOSIZE);
SetWindowPos(self.Handle, HWND_NOTOPMOST,0,0,0,0, SWP_SHOWWINDOW or SWP_NOMOVE or SWP_NOSIZE);

Edit Ok J'ai découvert un problème avec cela. L'application est mise en avant mais le focus est conservé dans une application originale . Utilisez cette réponse pour résoudre le problème, sa méthode assez complexe mais le copypaste fait l'affaire. Avant d'appeler ForceForegroundWindow (wnd), vous devrez peut-être appeler Application.Restore en minimisant une fenêtre. https://stackoverflow.com/a/5885318/185565

3
Whome

Vous pouvez écrire un exécutable pour contourner cette limitation. Prenons par exemple une application simple (non visuelle) (essentiellement une application console mais sans la {$ APPTYPE CONSOLE}) dans le seul but de faire apparaître la fenêtre souhaitée.

Votre application d'arrière-plan de surveillance appelle l'application d'assistance via un appel en ligne de commande (par exemple ShellExecute) chaque fois que votre action de mise en avant est nécessaire. Étant donné que cette application devient le processus de premier plan, elle est alors en mesure de mettre en avant d'autres fenêtres (SetForeGroundWindow). Vous pouvez utiliser FindWindow pour obtenir une poignée sur votre fenêtre cible ou transmettre les paramètres appropriés lors du démarrage de votre application d'assistance.

3
Erik Virtel
function WinActivate(const AWinTitle: string): boolean;
var
  _WindowHandle: HWND;
begin
  Result := false;
  _WindowHandle := FindWindow(nil, PWideChar(AWinTitle));
  if _WindowHandle <> 0 then
  begin
    if IsIconic(_WindowHandle) then
      Result := ShowWindow(_WindowHandle, SW_RESTORE)
    else
      Result := SetForegroundWindow(_WindowHandle);
  end;
end;

if WinActivate(self.Caption) then
  ShowMessage('This works for me in Windows 10');
3

En supposant qu'aucune autre application StayOnTop ne s'exécute, vous pouvez temporairement définir vos formulaires FormStyle sur fsStayOnTop, puis faire et Application Restore et BringToFront, puis modifier le style de formulaire pour qu'il soit.

tmpFormStyle := Application.MainForm.FormStyle;
Application.MainForm.FormStyle := fsStayOnTop;
Application.Restore; // optional
Application.BringToFront;
Application.MainForm.FormStyle := tmpFormStyle;

Encore une fois, cela ne fonctionne que s'il n'y a pas d'autres applications stayontop.

2
Toby