none
Situer une fenêtre sur l'écran (des tests seraient bienvenus sous Aero) RRS feed

  • Question

  • Bonjour tout le monde,

    J'ai écrit un programme pour contrôler la présence de mails sur une boîte, et dans l'affirmative ouvrir Thunderbird.

    Ça fait ça, mais avec un effet de bord. Je m'aperçois que la fenêtre Thunderbird devient toute petite (en taille "intermédiaire"), et va même jusqu'à se cacher derrière la fenêtre de la loupe. Pratique. J'ai montré à l'utilisatrice comment ramener ça à un endroit visible en utilisant le menu système de la fenêtre, mais il n'empêche que ça fait désordre.

    Sur la machine de développement en Windows 10 le problème ne se pose pas, ce qui ne m'étonne pas car dans le programme je n'ai mis aucune instruction pour modifier le positionnement de Thunderbird en taille intermédiaire. J'agrandis la fenêtre principale Thunderbird, mais que je sache ce n'est pas supposé avoir une influence sur la taille intermédiaire. Raison pour laquelle j'ai lancé un antivirus sur la machine d'utilisation (sous Vista), mais l'antivirus n'a rien trouvé. J'aurais bien lancé adwCleaner, qui n'a rien fait. Sur le moment je ne me suis pas rappelé qu'il fallait charger une ancienne version car les dernières ne sont pas compatibles avec Vista. De toute manière il y a deux semaines la version compatible disait que tout allait bien.

    Alors du coup, je mettrais bien dans mon programme les instructions pour indiquer la position de la fenêtre Thunderbird.

    J'ai essayé avec GetWindowPos, qui m'a donné quelque chose de délirant sur la machine de développement (-21000 et quelque chose pour chaque nombre). La doc dit que c'est normal car j'ai installé Vista Aero sur mon Windows 10, pour pouvoir gérer quelques tailles qui posaient problème.

    J'ai essayé de passer par GetPlacement, mais les valeurs pour être différentes ne sont pas beaucoup plus convaincantes : les quatre nombres sont à 0.

    On va bien avoir encore une API qui fonctionne, pour situer une fenêtre sur l'écran ?

    ***

    Hum ... Début de piste sur GetPlacement ...

    La signature est

    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool GetWindowPlacement(IntPtr hWnd, ref WINDOWPLACEMENT lpwndpl);

    et lors de la compilation j'ai comme erreur :

    Erreur	CS1615	L'argument 2 ne doit pas être passé avec le mot clé 'ref

    Bon, alors puisqu'on accuse la documentation, voilà l'adresse :

    https://www.pinvoke.net/default.aspx/user32/GetWindowPlacement.html

    C'est vrai que ça ne m'empêcherait pas d'afficher les valeurs dans la fonction qui appelle GetPlacement, mais j'ai l'habitude de mettre ça dans un fichier winapi, qui n'a pas vocation à exploiter les données, juste à les lire.

    Donc, tant qu'à lire les coordonnées de la fenêtre, autant le faire bien.



    • Modifié Gloops lundi 15 juin 2020 09:02
    mercredi 6 mai 2020 12:34

Réponses

  • La doc explique la différence possible :

    "

    If the window is a top-level window that does not have the WS_EX_TOOLWINDOW window style, then the coordinates represented by the following members are in workspace coordinates: ptMinPosition, ptMaxPosition, and rcNormalPosition. Otherwise, these members are in screen coordinates.

    Workspace coordinates differ from screen coordinates in that they take the locations and sizes of application toolbars (including the taskbar) into account. Workspace coordinate (0,0) is the upper-left corner of the workspace area, the area of the screen not being used by application toolbars.

    "

    Sinon sur ma config, j'obtiens les mêmes coordonnées sur Notepad =>

    IntPtr hWndDest = FindWindow("Notepad", null);
    RECT rcWindow;
    GetWindowRect(hWndDest, out rcWindow);
    WINDOWPLACEMENT wp = new WINDOWPLACEMENT();
    wp.length = (uint)Marshal.SizeOf(typeof(WINDOWPLACEMENT));
    GetWindowPlacement(hWndDest, out wp);

    Declarations :

            [DllImport("User32.dll", SetLastError = true)]
            public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
    
            [DllImport("User32.dll", SetLastError = true)]
            public static extern bool GetClientRect(IntPtr hWnd, out RECT lpRect);
    
            [DllImport("User32.dll", SetLastError = true)]
            public static extern bool GetWindowPlacement(IntPtr hWnd, out WINDOWPLACEMENT lpwndpl);
    
            [StructLayout(LayoutKind.Sequential)]
            public struct WINDOWPLACEMENT
            {
                public uint length;
                public uint flags;
                public uint showCmd;
                public System.Drawing.Point ptMinPosition;
                public System.Drawing.Point ptMaxPosition;
                public RECT rcNormalPosition;
                public RECT rcDevice;
            }
    
            public const int WPF_SETMINPOSITION = 0x0001;
            public const int WPF_RESTORETOMAXIMIZED = 0x0002;
            public const int WPF_ASYNCWINDOWPLACEMENT = 0x0004;

    • Marqué comme réponse Gloops lundi 15 juin 2020 08:49
    dimanche 10 mai 2020 11:11
  • Et avec DwmGetWindowAttribute, j'ai une différence de -7, ce qui est normal (Windows 10) =>

    RECT rcFrameBounds = new RECT();               
    HRESULT hr = DwmGetWindowAttribute(hWndDest, (int)DWMWINDOWATTRIBUTE.DWMWA_EXTENDED_FRAME_BOUNDS, ref rcFrameBounds, Marshal.SizeOf(typeof(RECT)));

            public enum HRESULT : int
            {
                S_OK = 0,
                S_FALSE = 1,
                E_NOINTERFACE = unchecked((int)0x80004002),
                E_NOTIMPL = unchecked((int)0x80004001),
                E_FAIL = unchecked((int)0x80004005),
                E_UNEXPECTED = unchecked((int)0x8000FFFF),
                E_OUTOFMEMORY = unchecked((int)0x8007000E)
            }
            
            [DllImport("Dwmapi.dll", SetLastError = true)]
            public static extern HRESULT DwmGetWindowAttribute(IntPtr hwnd, int dwAttributeToGet, ref int pvAttributeValue, int cbAttribute);
    
            [DllImport("Dwmapi.dll", SetLastError = true)]
            public static extern HRESULT DwmGetWindowAttribute(IntPtr hwnd, int dwAttributeToGet, ref RECT pvAttributeValue, int cbAttribute);
    
            [DllImport("Dwmapi.dll", SetLastError = true)]
            public static extern HRESULT DwmSetWindowAttribute(IntPtr hwnd, int dwAttributeToGet, ref int pvAttributeValue, int cbAttribute);
    
            public enum DWMWINDOWATTRIBUTE
            {
                DWMWA_NCRENDERING_ENABLED = 1,
                DWMWA_NCRENDERING_POLICY,
                DWMWA_TRANSITIONS_FORCEDISABLED,
                DWMWA_ALLOW_NCPAINT,
                DWMWA_CAPTION_BUTTON_BOUNDS,
                DWMWA_NONCLIENT_RTL_LAYOUT,
                DWMWA_FORCE_ICONIC_REPRESENTATION,
                DWMWA_FLIP3D_POLICY,
                DWMWA_EXTENDED_FRAME_BOUNDS,
                DWMWA_HAS_ICONIC_BITMAP,
                DWMWA_DISALLOW_PEEK,
                DWMWA_EXCLUDED_FROM_PEEK,
                DWMWA_CLOAK,
                DWMWA_CLOAKED,
                DWMWA_FREEZE_REPRESENTATION,
                DWMWA_LAST
            }

    • Marqué comme réponse Gloops lundi 15 juin 2020 08:55
    lundi 11 mai 2020 13:07
  • En parcourant le forum je me rappelle que le fil est toujours marqué non résolu, alors que la prochaine étape serait que je passe plus de temps dessus pour appliquer les solutions proposées, et évaluer l'impact d'Aéro sur la question.

    Donc je marque au moins une réponse qui est un bon point de départ, et le prochain qui s'intéressera à la question pourra tâtonner surtout si il utilise aussi Aero. Il se peut que ce soit moi, après encore quelques semaines.

    Pour la petite histoire, le projet qui a motivé le fil a subi des aléas dus à l'installation (temporairement) d'une version plus récente de .Net par un programme de sauvegarde. Ça s'est résolu par désinstaller les versions de .Net en descendant jusqu'à la 4 puis réinstaller en remontant.

    À présent ça se passe bien, il faudra que je pense à faire une piqûre de rappel auprès de l'utilisatrice au sujet du positionnement de la fenêtre de la loupe -en barre d'outils ou pas. J'ai une fenêtre d'aide qui se positionne maintenant bien, alors que sans que je comprenne pourquoi le positionnement dépendait du répertoire dans lequel était stocké le fichier d'aide.

    • Marqué comme réponse Gloops lundi 15 juin 2020 08:55
    • Modifié Gloops lundi 15 juin 2020 09:11
    lundi 15 juin 2020 08:54

Toutes les réponses

  • Pour finir j'ai confondu GetWindowPlacement, qui est l'API, avec GetPlacement, qui est la fonction à appeler depuis le programme (l'une appelant l'autre).

    Après avoir passé des paramètres cohérents ça donne des nombres qui me surprennent un peu, mais qui sont quand même dans le domaine du possible.

    À gauche j'ai 0 alors qu'il y a un peu de marge, mettons que ça ne soit pas tout-à-fait le même repère, j'ai déjà vu des choses comme ça en gérant des barres d'outils.

    En bas j'ai 678 alors que l'écran fait 1080 de haut et que sous la fenêtre Thunderbird il y a la barre des tâches Windows (double ligne) et la barre d'état de Firefox. Donc, j'aurais peut-être plutôt deviné 900 et quelque chose, mais c'est déjà mieux que -31000.

    Je vais voir ce que me dit la machine utilisatrice avec ça.

    mercredi 6 mai 2020 13:27
  • Sur Vista les valeurs indiquées sont ... franchement bizarres, du genre valeur négative pour la droite.

    En revanche, le simple fait d'avoir mis un MessageBox après l'ouverture de Thunderbird évite qu'il aille se cacher derrière la loupe.

    Je remplace le MessageBox par System.Threading.Thread.Sleep(1000), et ça continue à fonctionner.

    J'agrandissais la fenêtre principale Thunderbird avant qu'elle ait eu le temps de s'ouvrir, et ça, ça donne des résultats imprévisibles (ce qui peut aussi signifier différents d'un système à l'autre).

    Encore une chose : sur mon Windows 10, les MessageBox se mettent à s'afficher avec une fenêtre de titre blanche, alors que normalement elle est rouge, et en plus une fois sur deux ça va se nicher derrière d'autres fenêtres et je n'arrive plus à mettre le focus dessus. Peut-on hasarder une explication à ça ?

    Alors récapitulons :

    • J'ai débogué mon application de nombre de mails ; l'affichage des coordonnées de la fenêtre m'a mis sur la voie, même si il est délirant
    • Je n'ai pas l'impression d'avoir lu des coordonnées correctes pour une fenêtre ; pour ce qui m'occupe aujourd'hui ça devient moins urgent, mais à l'occasion ça peut rendre service
    • J'ai un autre souci sur Windows 10, les MessageBox s'affichent bizarrement, un peu comme si la mémoire qui leur est allouée était insuffisante.


    • Modifié Gloops mercredi 6 mai 2020 14:09
    mercredi 6 mai 2020 14:04
  • On va bien avoir encore une API qui fonctionne, pour situer une fenêtre sur l'écran ?

    Simplement GetWindowRect

    Test avec Notepad lancé =>

    (ne tient pas compte de DWM (x commence à -7), sinon, il faut corriger avec DwmGetWindowAttribute et DWMWA_EXTENDED_FRAME_BOUNDS)

    IntPtr hWndDest = FindWindow("Notepad", null);
    RECT rcWindow;
    GetWindowRect(hWndDest, out rcWindow);

    avec déclarations :

    [StructLayout(LayoutKind.Sequential)]
    public struct RECT
    {
        public int left;
        public int top;
        public int right;
        public int bottom;
        public RECT(int Left, int Top, int Right, int Bottom)
        {
            left = Left;
            top = Top;
            right = Right;
            bottom = Bottom;
        }
    }
    
    
    [DllImport("User32.dll", SetLastError = true)]
    public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
    
    [DllImport("User32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
    public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);


    mercredi 6 mai 2020 15:19
  • Bon, maintenant que j'ai pu monter le ListView, le enumWindows et tout ce qui va bien, j'ai pu afficher les coordonnées de mon Notepad2 (Left, Top, Right, Bottom) :

    -32000 ; -32000 ; -31763 ; -31959

    Est-ce que GetWindowRect est supposé être compatible avec Aero ?

    La fenêtre démarre tout en haut (un poil plus bas, mais ça devrait faire 0), et pour la gauche on est dans le menu Affichage de Firefox. À vue de nez, à peu près autant de marge à droite et en bas qu'à gauche.

    [HS] Pendant qu'on parle de ListView, il accepte une propriété BackColor pour la couleur de fond de la zone de données, mais si je veux colorier les entêtes de colonnes, il faut soit que je leur associe une image de fond, soit que je fasse une gymnastique pour récupérer le hWnd de chaque et colorier avec les API.

    Est-ce que j'ai mal lu ?


    • Modifié Gloops samedi 9 mai 2020 20:33
    samedi 9 mai 2020 20:18
  • Quand GetWindowRect ramène -32000, c'est que la fenêtre est réduite (du moins sous Windows 10)
    samedi 9 mai 2020 20:40
  • Bien vu ! Complètement oublié de vérifier ça.

    123 ; 7 ; 1030 ; 492

    Ça sonne plus juste, pas vrai ?

    La définition de l'écran est de 1920 x 1080.

    Tiens, du coup 492 me paraît faire peu si c'est compté à partir du haut.

    Mais le reste paraît tenir la route.

    Il faudra que je compare plus précisément avec GetWindowPlacement, qui lui se contre-fiche de l'état dans lequel se trouve la fenêtre, tant qu'elle n'est pas fermée.



    • Modifié Gloops samedi 9 mai 2020 20:53
    samedi 9 mai 2020 20:46
  • Bonjour,

    Voilà le résultat :

    123 ; 7 ; 1030 ; 492 (GetWindowRect)
    123 ; 7 ; 1153 ; 499 (GetWindowPlacement, rcNormalPosition)
    

    ce qui laisse entendre, sans avoir relu la doc, que l'un mesure la fenêtre entière, et l'autre la zone cliente ?

    Mouaif, c'est une explication facile sortie vite fait d'un chapeau, mais horizontalement 123 ça fait plus que la zone des numéros de lignes, qui d'ailleurs est à gauche, et verticalement 7, a priori ça fait juste la barre d'état.

    À moins qu'il y ait plus sioux que ça comme explication.

    dimanche 10 mai 2020 10:45
  • La doc explique la différence possible :

    "

    If the window is a top-level window that does not have the WS_EX_TOOLWINDOW window style, then the coordinates represented by the following members are in workspace coordinates: ptMinPosition, ptMaxPosition, and rcNormalPosition. Otherwise, these members are in screen coordinates.

    Workspace coordinates differ from screen coordinates in that they take the locations and sizes of application toolbars (including the taskbar) into account. Workspace coordinate (0,0) is the upper-left corner of the workspace area, the area of the screen not being used by application toolbars.

    "

    Sinon sur ma config, j'obtiens les mêmes coordonnées sur Notepad =>

    IntPtr hWndDest = FindWindow("Notepad", null);
    RECT rcWindow;
    GetWindowRect(hWndDest, out rcWindow);
    WINDOWPLACEMENT wp = new WINDOWPLACEMENT();
    wp.length = (uint)Marshal.SizeOf(typeof(WINDOWPLACEMENT));
    GetWindowPlacement(hWndDest, out wp);

    Declarations :

            [DllImport("User32.dll", SetLastError = true)]
            public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
    
            [DllImport("User32.dll", SetLastError = true)]
            public static extern bool GetClientRect(IntPtr hWnd, out RECT lpRect);
    
            [DllImport("User32.dll", SetLastError = true)]
            public static extern bool GetWindowPlacement(IntPtr hWnd, out WINDOWPLACEMENT lpwndpl);
    
            [StructLayout(LayoutKind.Sequential)]
            public struct WINDOWPLACEMENT
            {
                public uint length;
                public uint flags;
                public uint showCmd;
                public System.Drawing.Point ptMinPosition;
                public System.Drawing.Point ptMaxPosition;
                public RECT rcNormalPosition;
                public RECT rcDevice;
            }
    
            public const int WPF_SETMINPOSITION = 0x0001;
            public const int WPF_RESTORETOMAXIMIZED = 0x0002;
            public const int WPF_ASYNCWINDOWPLACEMENT = 0x0004;

    • Marqué comme réponse Gloops lundi 15 juin 2020 08:49
    dimanche 10 mai 2020 11:11
  • Effectivement, Notepad n'a pas de barres d'outils, alors que Notepad2, si.

    C'est pas mal déroutant, cette affaire-là : les barres d'outils sont en haut, et pourtant c'est en bas que rcNormalPosition dépasse GetWindowRect.

    Et il n'y a rien à droite, alors que c'est là que ça dépasse le plus.

    On dirait que si on veut vraiment être sûr de tout comprendre il faut expérimenter en traçant les différents rectangles.

    On dirait que ça risque d'être pour plus tard, car j'ai pris du retard sur ce que je dois faire en web avant que ma machine passe en révision.

    En tout cas merci pour l'écho, je suis allé bien plus loin que je ne l'aurais fait tout seul.

    dimanche 10 mai 2020 14:12
  • Bonjour,

    Finalement, j'ai tracé ces deux fichus rectangles, "brut de fonderie".

    				Graphics vGraphics = Graphics.FromHwnd(Handle);
    				vGraphics.DrawRectangle(Pens.Blue, new Rectangle(
    					wp.rcNormalPosition.Left,
    					wp.rcNormalPosition.Top,
    					wp.rcNormalPosition.Right,
    					wp.rcNormalPosition.Bottom));
    				vGraphics.DrawRectangle(Pens.Red, new Rectangle(
    					W.Left,
    					W.Top,
    					W.Right,
    					W.Bottom));

    Le W a été renseigné depuis GetWindowRect.

    Les deux rectangles commencent dans le coin inférieur gauche de la barre de titre.

    Le bleu dépasse le coin inférieur droit de l'écran, le rouge s'arrête juste un peu avant. Les deux dépassent donc généreusement la fenêtre qu'ils mesurent.

    On ne dit rien sur pinvoke.net, mais je soupçonne que j'ai un changement d'échelle à faire.

    Pourtant si c'est ça l'explication on devrait aussi avoir un décalage dans le coin supérieur gauche.

    Je précise que les rectangles sont tracés sur le formulaire de l'application de mesure, que pour l'occasion je rends le ListView invisible pour éviter qu'il masque une partie de ce qu'on a à voir.

    Il a fallu agrandir la fenêtre, car les coordonnées sont relatives à cette fenêtre.

    Donc, si je remonte de la hauteur de la barre de titre de l'application de mesure, ça me fait démarrer le rectangle dans le coin supérieur gauche de la fenêtre mesurée, celle de Notepad2.

    Pour le coin inférieur droit je n'ai rien eu de convaincant.


    • Modifié Gloops lundi 11 mai 2020 11:04
    lundi 11 mai 2020 11:00
  • La doc explique la différence possible :

    "

    Workspace coordinates differ from screen coordinates in that they take the locations and sizes of application toolbars (including the taskbar) into account. Workspace coordinate (0,0) is the upper-left corner of the workspace area, the area of the screen not being used by application toolbars.

    "


    En dehors de la fenêtre de Notepad2, ce que j'ai comme "barres d'outils" sur l'écran, c'est juste la barre des tâches, elle est en bas.

    lundi 11 mai 2020 11:56
  • Je viens de tester avec Notepad2, et j'obtiens exactement les mêmes résultats qu'avec Notepad =>

    En Debug =>

    lundi 11 mai 2020 12:39
  • Serait-ce Aero qui perturbe ?

    Il y a des paramètres qu'on ne peut régler que par là ...

    lundi 11 mai 2020 12:48
  • Et avec DwmGetWindowAttribute, j'ai une différence de -7, ce qui est normal (Windows 10) =>

    RECT rcFrameBounds = new RECT();               
    HRESULT hr = DwmGetWindowAttribute(hWndDest, (int)DWMWINDOWATTRIBUTE.DWMWA_EXTENDED_FRAME_BOUNDS, ref rcFrameBounds, Marshal.SizeOf(typeof(RECT)));

            public enum HRESULT : int
            {
                S_OK = 0,
                S_FALSE = 1,
                E_NOINTERFACE = unchecked((int)0x80004002),
                E_NOTIMPL = unchecked((int)0x80004001),
                E_FAIL = unchecked((int)0x80004005),
                E_UNEXPECTED = unchecked((int)0x8000FFFF),
                E_OUTOFMEMORY = unchecked((int)0x8007000E)
            }
            
            [DllImport("Dwmapi.dll", SetLastError = true)]
            public static extern HRESULT DwmGetWindowAttribute(IntPtr hwnd, int dwAttributeToGet, ref int pvAttributeValue, int cbAttribute);
    
            [DllImport("Dwmapi.dll", SetLastError = true)]
            public static extern HRESULT DwmGetWindowAttribute(IntPtr hwnd, int dwAttributeToGet, ref RECT pvAttributeValue, int cbAttribute);
    
            [DllImport("Dwmapi.dll", SetLastError = true)]
            public static extern HRESULT DwmSetWindowAttribute(IntPtr hwnd, int dwAttributeToGet, ref int pvAttributeValue, int cbAttribute);
    
            public enum DWMWINDOWATTRIBUTE
            {
                DWMWA_NCRENDERING_ENABLED = 1,
                DWMWA_NCRENDERING_POLICY,
                DWMWA_TRANSITIONS_FORCEDISABLED,
                DWMWA_ALLOW_NCPAINT,
                DWMWA_CAPTION_BUTTON_BOUNDS,
                DWMWA_NONCLIENT_RTL_LAYOUT,
                DWMWA_FORCE_ICONIC_REPRESENTATION,
                DWMWA_FLIP3D_POLICY,
                DWMWA_EXTENDED_FRAME_BOUNDS,
                DWMWA_HAS_ICONIC_BITMAP,
                DWMWA_DISALLOW_PEEK,
                DWMWA_EXCLUDED_FROM_PEEK,
                DWMWA_CLOAK,
                DWMWA_CLOAKED,
                DWMWA_FREEZE_REPRESENTATION,
                DWMWA_LAST
            }

    • Marqué comme réponse Gloops lundi 15 juin 2020 08:55
    lundi 11 mai 2020 13:07
  • Je me serais attendu à 5, mais je chipotons pas.

    Sur une autre fonction la doc signale qu'il faut se méfier d'Aéro -que justement j'utilise.

    Est-ce qu'il est installé?

    lundi 11 mai 2020 13:23
  • En parcourant le forum je me rappelle que le fil est toujours marqué non résolu, alors que la prochaine étape serait que je passe plus de temps dessus pour appliquer les solutions proposées, et évaluer l'impact d'Aéro sur la question.

    Donc je marque au moins une réponse qui est un bon point de départ, et le prochain qui s'intéressera à la question pourra tâtonner surtout si il utilise aussi Aero. Il se peut que ce soit moi, après encore quelques semaines.

    Pour la petite histoire, le projet qui a motivé le fil a subi des aléas dus à l'installation (temporairement) d'une version plus récente de .Net par un programme de sauvegarde. Ça s'est résolu par désinstaller les versions de .Net en descendant jusqu'à la 4 puis réinstaller en remontant.

    À présent ça se passe bien, il faudra que je pense à faire une piqûre de rappel auprès de l'utilisatrice au sujet du positionnement de la fenêtre de la loupe -en barre d'outils ou pas. J'ai une fenêtre d'aide qui se positionne maintenant bien, alors que sans que je comprenne pourquoi le positionnement dépendait du répertoire dans lequel était stocké le fichier d'aide.

    • Marqué comme réponse Gloops lundi 15 juin 2020 08:55
    • Modifié Gloops lundi 15 juin 2020 09:11
    lundi 15 juin 2020 08:54