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) .
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.
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;
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
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.
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');
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.