Modifier Z Order Windows avec JNA

Oliv -  
 Oliv -
Bonjour à toutes et à tous,

je voudrais pouvoir inverser le comportement des fenêtres dans Windows (avec JNA donc) grâce à une application java, quand elle est active. Je parle bien des fenêtres ouvertes (réduites ou non) sur le bureau, qu'elles proviennent d'applis java ou non.
Le comportement que je voudrais leur donner consisterais à donner à toute fenêtre restaurée la dernière position du Z Order.
C'est-à-dire que si j'ai deux applis lancées, l'une dont la fenêtre est seule visible à l'écran, l'autre dont la fenêtre est réduite à son icône dans la barre des tâches, et que je clique sur cette icône pour restaurer cette fenêtre, celle-ci devrait apparaître à l'écran DERRIÈRE la fenêtre qui était déjà visible et ne pas prendre le focus. Même chose pour une appli avec plusieures fenêtres, plusieures applis avec plusieures fenêtres, et même chose aussi si la restauration est spontanée (non provoquée par une action de l'utilisateur).
Il y a quelques temps, j'avais fait un bidouillage, consistant à recupérer et stocker en boucle le nom des fenêtres visibles, comparer la première d'entre elles avec la première du tour de boucle précédant, et en cas de changement, envoyer la nouvelle en dernière position.
Le "problème" étant que j'interviens APRÈS qu'elle soit d'abord passée par le premier plan et aie brièvement pris le focus.
Dans certains cas, ce brièvement n'est pas assez court, et provoque des erreurs de manipulation.
Est-il possible de faire ça plus proprement ?
Merci de toute aide, tuyau, conseil...
A voir également:

1 réponse

KX Messages postés 16761 Date d'inscription   Statut Modérateur Dernière intervention   3 020
 
Bonjour,

Le JNA c'est une technologie pour manipuler du code Windows en Java, mais ta question relève plus de comment le faire avec Windows que comment le faire en Java.

Ce serait peut-être donc plus une question pour un forum Windows que pour un forum Java.

Concernant JNA, voici un exemple qui manipule GetForegroundWindow
import java.awt.Rectangle;
import com.sun.jna.Native;
import com.sun.jna.platform.win32.WinDef.HWND;
import com.sun.jna.platform.win32.WinDef.RECT;

public class WindowsUtils {
    private static class User32Dll {
        static {
            Native.register("user32");
        }
        
        public static native HWND GetForegroundWindow();
        public static native boolean GetWindowRect(HWND hWnd, RECT lpRect);
    }
    
    /** @return le rectangle représentant la position et la taille de la fenêtre active */
    public static Rectangle getActiveWindowPosition(){
        RECT rect = new RECT();
        User32Dll.GetWindowRect(User32Dll.GetForegroundWindow(), rect);
        return new Rectangle(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
    }
}
0
Oliv
 
Merci à toi. Cependant, comme décrit çi-avant, connaitre le handle de la fenêtre de premier plan (et donc titre, style, position, dimensions, etc...) ne pose pas de problème (j'avais opté pour l'énumération pour profiter de la boucle).
D'autre part le code sera en java parce que c'est le seul langage qui ne m'est pas totalement étranger (je ne vais pas apprendre tout un langage aussi complexe et risqué que le C -comparativement- pour un seul et minuscule petit programme.
Ce dont j'ai besoin, c'est que cette fameuse fenêtre reste toujours au premier plan, même si d'autres fenêtres, venant d'autres applications ou non, venaient à être ouvertes ou restaurées.
Autrement dit comment faire pour qu'une fenêtre ayant le focus sur le bureau ne le perde jamais et ne sois jamais recouverte, quoiqu'il arrive tant qu'elle est affichée à l'écran.
Merci encore !
0
Oliv
 
Ah j'ajoute que si je pose la question dans la section Windows, je ne comprendrais rien à la réponse, aussi juste, précise, détaillée, voire pré-machée, soit-elle.
Il aurait fallu une section Java Native Access (et donc 1 000 autres dérivés du même genre, ce qui ne ferait pas très sérieux).
Mais j'avais quand même tagué "Windows" hein ?
Blague à part, merci de t'être déjà penché sur mon cas.
0
KX Messages postés 16761 Date d'inscription   Statut Modérateur Dernière intervention   3 020
 
La partie Java ne posera aucun problème, c'est juste une bascule vers les api natives de Windows.
Toute la difficulté sera de trouver dans les api Windows les bonnes méthodes qui feront ce que tu veux.
Soit en fouillant la documentation (qui est faite pour ça) soit en se renseignant auprès de ceux qui s'en servent, peut-être des développeurs C++ ? Mais en tout cas, ce n'est pas sur un forum Java qu'on pourra te renseigner.
0
Oliv > KX Messages postés 16761 Date d'inscription   Statut Modérateur Dernière intervention  
 
Ok, merci.
Je voudrais quand même laisser un petit moment la question ouverte, car c'est en posant cette même question dans les mêmes lieux et conditions que j'avais à l'époque pu mettre au point la méthode décrite, qui fait exactement ce que je veux APRÈS l'apparition d'une fenêtre (JUSTE après, voire pendant, suivant l'animation, mais pas avant).
Je répète que je ne comprend rien au C, c'est le JNA (oui il appelle le C, mais surtout, il le manipule -pointeurs et consorts) qu'il me faut. C'est-à-dire que si tu m'avais donné le code C pour récupérer la fenêtre de premier plan, je n'aurais rien pu en faire, contrairement à celui que tu m'a donné.

Mais... Je vais peut-être AUSSI voir du côté du forum Windows non pas du point de vue programmation, mais du logiciel. Peut-être qu'il existe un moyen de changer le comportement par défaut des fenêtres, car après tout, le fait qu'elles soit envoyées au premier plan et prennent le focus, c'est déjà un réglage. Peut-on y accéder ? Par les registres ? (J'aimerais pas ça mais bon...)
Je mettrais le lien de l'autre ici et vice-versa, puis signalerais l'éventuelle réponse à ma portée, non programmatique ici si il y en a une, disons quelques jours pour ceux éventuellement qui suivent sans se manifester, avant de supprimer cette discussion en tant que doublon ; ou vice-versa.

Puis-je ?
0
Oliv
 
Bon, pour le comportement par défaut des fenêtres, je vais oublier.
Personne ne peut m'aider ?
Le code que j'avais mis au point est le suivant :

package zOrder;

import java.awt.AWTException;
import java.awt.Image;
import java.awt.MenuItem;
import java.awt.PopupMenu;
import java.awt.Rectangle;
import java.awt.SystemTray;
import java.awt.Toolkit;
import java.awt.TrayIcon;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;

import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import com.sun.jna.platform.win32.WinDef.BOOL;
import com.sun.jna.platform.win32.WinDef.HWND;
import com.sun.jna.platform.win32.WinDef.POINT;
import com.sun.jna.platform.win32.WinDef.RECT;
import com.sun.jna.platform.win32.WinUser;
import com.sun.jna.platform.win32.WinUser.WNDENUMPROC;
import com.sun.jna.win32.StdCallLibrary;
import com.sun.jna.win32.W32APIOptions;

final class ZOrder {
 private static final ArrayList<HWND>HANDLES = new ArrayList<HWND>();
 private static int windowsSize;
 private static class ZOrderInverter {
  private static TrayIcon tray;
  private static PopupMenu popup = new PopupMenu();
  static {
   MenuItem _close = new MenuItem("Close");
   _close.addActionListener(new ActionListener(){
    public void actionPerformed(ActionEvent e) {
     SystemTray.getSystemTray().remove(ZOrderInverter.tray);
    }
   });
   popup.add(_close);
   if(SystemTray.isSupported() == true) {
    Image _icon = Toolkit.getDefaultToolkit().getImage (
     ZOrder.class.getResource("/rsrc/Z Order Inverter TrayIcon.png")
    );
    tray = new TrayIcon(_icon, "Z Order Inverter is running", popup);
    tray.setImageAutoSize(true);
    try {
     SystemTray.getSystemTray().add(ZOrderInverter.tray);
    } catch(AWTException e) {
     e.printStackTrace();
    }
   }
  }
  private static boolean isActive(){
   List<TrayIcon>trays_list = Arrays.asList(SystemTray.getSystemTray().getTrayIcons());
   return trays_list.contains(tray);
  }
 }
 private static class Window {
  private String name;
  private Window(String _name, Rectangle _bounds) {
   name = _name;
  }
 }
 private interface User32 extends StdCallLibrary {
  static final User32 INSTANCE = (User32)Native.load (
   "user32", User32.class, W32APIOptions.DEFAULT_OPTIONS
  );
  class WINDOWPLACEMENT extends Structure {
   public int length;
   public int flags;
   public int showCmd;
   public POINT ptMinPosition;
   public POINT ptMaxPosition;
   public RECT rcNormalPosition;
   private WINDOWPLACEMENT(){
    length = size();
    
    // Lignes totalement inutiles, juste pour ne pas avoir de @SuppressWarnings("unused")
    List<Object>_null = Arrays.asList (
     length, flags, showCmd, ptMinPosition, ptMaxPosition, rcNormalPosition
    );
    _null.isEmpty();
   }
   @Override
   protected List<String>getFieldOrder(){
    return Arrays.asList (
     "length", "flags", "showCmd", "ptMinPosition", "ptMaxPosition", "rcNormalPosition"
    );
   }
  }
  int GetWindowRect(HWND h_wnd, int _rect[]);
  int GetWindowTextA(HWND h_wnd, byte lp_string[], int n_max_count);
  boolean EnumWindows(WNDENUMPROC lp_enum_func, Pointer _pointer);
  boolean IsWindowVisible(HWND h_wnd);
  BOOL GetWindowPlacement(HWND h_wnd, WINDOWPLACEMENT lp_wnd_pl);
  BOOL SetWindowPos (
   HWND h_wnd, HWND h_wnd_insert_after, int _x, int _y, int _cx, int _cy, int u_flag
  );
 }
 private static ArrayList<Window>getWindows()  {
  HANDLES.clear();
  ArrayList<Window>_windows = new ArrayList<Window>();
  User32.INSTANCE.EnumWindows(new WNDENUMPROC(){
   @Override
   public boolean callback(HWND h_wnd, Pointer _pointer) {
    if(getWindow(h_wnd).name.isEmpty() == false) {
     HANDLES.add(h_wnd);
     _windows.add(new Window(getWindowName(h_wnd), getWindowBounds(h_wnd)));
    }
    return true;
   }
  }, null);
  for(int i = _windows.size() - 1; i >= 0; i--) {
   User32.WINDOWPLACEMENT _place = new User32.WINDOWPLACEMENT();
   User32.INSTANCE.GetWindowPlacement(HANDLES.get(i), _place);
   if(User32.INSTANCE.IsWindowVisible(HANDLES.get(i)) == false) {
    _windows.remove(i);
    HANDLES.remove(i);
   } else switch(_place.showCmd) {
   case 2:
    _windows.remove(i);
    HANDLES.remove(i);
    break;
   default:
    switch(_windows.get(i).name) {
    case "":
    case "Program Manager":
    case "Microsoft Text Input Application":
     _windows.remove(i);
     HANDLES.remove(i);
    }
    break;
   }
  }
  return _windows;
 }
 private static String getWindowName(HWND h_wnd) {
  byte window_text[] = new byte[512];
  User32.INSTANCE.GetWindowTextA(h_wnd, window_text, 512);
  return Native.toString(window_text);
 }
 private static Window getWindow(HWND h_wnd) {
  return new Window(getWindowName(h_wnd), getWindowBounds(h_wnd));
 }
 private static Rectangle getWindowBounds(HWND h_wnd) {
  int _rect[] = { 0, 0, 0, 0 };
  switch(User32.INSTANCE.GetWindowRect(h_wnd, _rect)) {
  case 0:
   return null;
  }
  return new Rectangle(_rect[0], _rect[1], _rect[2], _rect[3]);
 }
 public static void main(String _args[]) {
  SwingUtilities.invokeLater(new Runnable(){
   @Override
   public void run(){
    new ZOrderInverter();
   }
  });
  ArrayList<Window>_windows = getWindows();
  String _title = _windows.get(0).name;
  windowsSize = _windows.size();
  while(ZOrderInverter.isActive() == true) {
   switch(_windows.size()) {
   case 0:
    break;
   default:
    switch(_title.compareTo(_windows.get(0).name)) {
    case 0:
     break;
    default:
     _title = _windows.get(0).name;
     if(windowsSize < _windows.size()) {
      User32.INSTANCE.SetWindowPos (
       HANDLES.get(0)
       , HANDLES.get(HANDLES.size() - 1)
       , 0, 0, 0, 0
       , WinUser.SWP_NOMOVE | WinUser.SWP_NOSIZE
      );
      _title = _windows.get(1).name;
     }
     windowsSize = _windows.size();
     break;
    }
    break;
   }
   _windows = getWindows();
  }
  SwingUtilities.invokeLater(new Runnable(){
   @Override
   public void run(){
    JOptionPane.showMessageDialog (
     null
     , "Z Order Inverter is no more active and will close."
     , "Z Order Inverter"
     , JOptionPane.INFORMATION_MESSAGE
    );
   }
  });
 }
}


Ce serait mieux si je pouvais intervenir sur l'évènement qui restaure une fenêtre et non sur la liste des fenêtres non réduites.
Mais je ne vois rien nul part pour ça. C'est un hook, mais comment ?
S'il vous plait...
0