IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C++ Discussion :

[Windows 11] Fenêtre impossible à détruire ? Problème de rafraichissement ?


Sujet :

C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre confirmé
    Profil pro
    Inscrit en
    Février 2005
    Messages
    208
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2005
    Messages : 208
    Par défaut [Windows 11] Fenêtre impossible à détruire ? Problème de rafraichissement ?
    Bonjour à tous,

    Nostalgique du temps où il était possible d'avoir du contenu HTML en fond d'écran du bureau Windows, je cherche à reproduire ce comportement en créant une application affichant une fenêtre entre le fond d'écran et les icônes du bureau.
    Je programme en C++ (bon sang, bien 15 ans que j'y ai pas touché 🤪 ) sous Windows 11 avec Visual Studio Code.

    Pour créer cette fenêtre, j'utilise cette technique de ninja : https://www.codeproject.com/Articles...n-Windows-plus.
    Le handle retourné (wallpaper_hwnd) sert de parent à la fenêtre de mon application, créée grâce à la ligne suivante :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    HWND hwnd = CreateWindowEx( 0, CLASS_NAME, L"Ma fenêtre",  WS_CHILD | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, 800, 600, wallpaper_hwnd, NULL, hInstance, NULL );
    J'ai aussi ajouté une icône dans la zone de notification avec un menu me permettant de quitter l'appli.
    Lorsque je clique sur 'Quitter', j'appelle la fermeture de ma fenêtre grâce au message suivant :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    PostMessage(hwnd, WM_CLOSE, 0, 0);
    La réception du message de fermeture 'WM_CLOSE' entraine l'exécution du code suivant :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    case WM_CLOSE:
    {
        WriteToLog(L"Fenêtre en train de se fermer.");
        BOOL windowDestroyed = DestroyWindow(hwnd);
        if (windowDestroyed) {
            WriteToLog(L"\tFenêtre enfant correctement détruite");
        } else {
            WriteToLog(L"\tFenêtre enfant non détruite");
        }
        break;
    }
    Puis l'on passe dans le code lors de la réception du message de destruction de la fenêtre 'WM_DESTROY' :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    case WM_DESTROY:
    {
        WriteToLog(L"Je suis dans Destroy de la fenêtre fille");
     
        // Supprimer l'icône de la zone de notification
        Shell_NotifyIcon(NIM_DELETE, &g_nid);
     
        // Détruire et libérer la fenêtre parente wallpaper_hwnd si elle existe
        HWND wallpaper_hwnd = GetParent(hwnd);
        if (wallpaper_hwnd != NULL)
        {
            WriteToLog(L"J'ai trouvé une fenêtre parente, je cherche à la détruire");
            BOOL wallPaperDestroyed = DestroyWindow(wallpaper_hwnd);
            if (wallPaperDestroyed) {
                WriteToLog(L"\tFenêtre parente correctement détruite");
            } else {
                WriteToLog(L"\tFenêtre parente non détruite");
            }
        }
     
        // Changer la couleur de fond de la fenêtre
        HBRUSH hbrBackground = CreateSolidBrush(RGB(255, 0, 0)); // Rouge
        SetClassLongPtr(hwnd, GCLP_HBRBACKGROUND, (LONG_PTR)hbrBackground);
     
        // Forcer le rafraichissement de la fenêtre enfant
        InvalidateRect(hwnd, NULL, TRUE);
     
        // Forcer le rafraichissement de la fenêtre parent
        InvalidateRect(wallpaper_hwnd, NULL, TRUE);
     
        PostQuitMessage(0);
        break;
    }
    Et c'est là que le bas blesse.
    Le retour dans le terminal m'indique que mon application c'est quittée normalement, mais je vois toujours un carré blanc dégueulasse entre mon fond d'écran et les icônes du bureau.
    Les retours dans le fichier de log m'indiquent :

    • Que 'Fenêtre parente non détruite'. Je m'y attendais, c'est 'ProgMan' qui la créé je ne dois pas pouvoir la détruire comme ça.
    • Que 'Fenêtre enfant correctement détruite'.

    La tentative de changement de couleur du fond de la fenêtre enfant dans 'WM_DESTROY' échoue, alors qu'il fonctionne avant la réception du message de fermeture de la fenêtre 'WM_CLOSE'.
    Est-ce là une preuve supplémentaire que ma fenêtre fille est bien détruite ?

    Est-ce un problème de rafraichissement de la fenêtre parent ?
    Une piste pour me permettre de me retrouver avec un espace tout propre et tout vide entre mon arrière plan de bureau et mes icônes ?

    Merci d'avance 😙

  2. #2
    Expert confirmé
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Février 2005
    Messages
    5 520
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Conseil

    Informations forums :
    Inscription : Février 2005
    Messages : 5 520
    Par défaut
    Je comprends pas trop la logique de votre programme.

    Vous créez une fenêtre selon un procédé (SendMessage vers un autre "applicatif"), alors pourquoi tenté de la détruire via un "DestroyWindow" bien sale ?
    Espionnez encore un peu, via Spy++, pour voir comment l'application de changement de fond d'écran fait le taf et faites pareil.

    Est-ce là une preuve supplémentaire que ma fenêtre fille est bien détruite ?
    Pas forcément, mais comme vous testez la valeur de retour de "DestroyWindow" ligne 4 du code "case WM_CLOSE:", pourquoi en douter ?
    Testez systématiquement les valeurs de retour et utiliser GetLastError pour avoir des explications aux éventuelles problèmes (quitte à utiliser des MACRO type SUCCEEDED)

    Avec 1 ou 2 copies d'écran, ça serait plus explicite pour nous.

  3. #3
    Membre confirmé
    Profil pro
    Inscrit en
    Février 2005
    Messages
    208
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2005
    Messages : 208
    Par défaut
    Bonjour bacelar et merci pour cette réponse,

    Citation Envoyé par bacelar Voir le message
    Espionnez encore un peu, via Spy++, pour voir comment l'application de changement de fond d'écran fait le taf et faites pareil...
    Il semblerait que Spy++ ne soit pas pris en compte par Visual Studio Code.

    Citation Envoyé par bacelar Voir le message
    pourquoi tenté de la détruire via un "DestroyWindow" bien sale ?
    Tout est dit : c'est une tentative bien sale, non fonctionnelle, pour résoudre mon problème.
    Ça ne coûtait pas bien cher de tenter.

    Après avoir relu attentivement le tuto originel posté plus haut, j'ai remarqué ceci :

    Note: Everything you draw onto this layer will stay there until you paint over it, invalidate it, or reset your wallpaper.
    J'ai donc récupéré le chemin vers l'image utilisé comme arrière plan en début de mon application grâce à :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    WCHAR wallpaperPath[MAX_PATH];
    SystemParametersInfo(SPI_GETDESKWALLPAPER, MAX_PATH, wallpaperPath, 0);
    Et je remets cette image en tant que fond d'écran avant de quitter l'application :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    SystemParametersInfo(SPI_SETDESKWALLPAPER, 0, wallpaperPath, SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);
    Je ne sais pas si c'est le plus propre, mais ça fonctionne 🤷
    J'ai tenté de rafraichir la fenêtre parente grâce à InvalidateRect et UpdateWindow après la destruction de la fenêtre fille, sans succès.

  4. #4
    Expert confirmé
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    Juillet 2013
    Messages
    4 772
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Analyste/ Programmeur

    Informations forums :
    Inscription : Juillet 2013
    Messages : 4 772
    Par défaut
    Citation Envoyé par themoye Voir le message
    J'ai tenté de rafraichir la fenêtre parente grâce à InvalidateRect et UpdateWindow après la destruction de la fenêtre fille, sans succès.
    Après on parle d'1 API qui a 30 ans (1992) et qui a été supplantée/ surcouchée plusieurs fois (MFC > WTL > WinForms > WPF, UWP) et donc il y a eu beaucoup de changements

    Mais il me semble que c'est normal ces 2 fonctions ne doivent pas être appelées directement (elles sont appelés en interne) et il me semble (et en lisant la documentation), il faut envoyer 1 message WM_PAINT.
    Après si tu veux forcer (en regardant vite fait sur Internet), il faut utiliser la fonction RedrawWindow (<- lien vers MSDN)


    Édit: on m'a mis -1 alors que personne ne peut expliquer pourquoi InvalidateRect et UpdateWindow ne fonctionnent pas.

  5. #5
    Membre confirmé
    Profil pro
    Inscrit en
    Février 2005
    Messages
    208
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2005
    Messages : 208
    Par défaut
    Hello foetus !

    Citation Envoyé par foetus Voir le message
    Ces 2 fonctions ne doivent pas être appelées directement (elles sont appelés en interne) et il me semble (et en lisant la documentation), il faut envoyer 1 message WM_PAINT.
    Ce n'est pas l'inverse ? 🤔
    Si je fais le test sur une fenêtre que j'ai créé, un appel à 'UpdateWindow' envoi le message 'WM_PAINT'.
    De la même manière qu'un appel à 'DestroyWindow' envoi le message 'WM_DESTROY' etc

  6. #6
    Expert confirmé
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    Juillet 2013
    Messages
    4 772
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Analyste/ Programmeur

    Informations forums :
    Inscription : Juillet 2013
    Messages : 4 772
    Par défaut
    Citation Envoyé par themoye Voir le message
    Ce n'est pas l'inverse ? 🤔
    Voici 1 commentaire sur stackoverflow qui reprend la documentation
    InvalidateRect() merely marks a window for repainting. The window is not actually repainted by the OS until the parent thread's message queue, or an explicit call to UpdateWindow() or RedrawWindow(), generates a new WM_PAINT to paint the window. At which time, anything that was on the window gets erased and redrawn. This is basic Win32 GDI stuff
    Donc voila, le fonctionnement est assez simple. Mais il y a 1 histoire de file de messages qui fait que ta fenêtre ignore "tes messages" tant qu'on ne lui pas dit de les traiter (en l'occurrence réception d'1 message WM_PAINT)


    Édit: Après il y a peut-être d'autres messages que WM_PAINT, comme WM_ERASEBKGND.
    Et ensuite, c'est peut-être 1 ""paramètrage"" du clipping fenêtre fille/ fenêtre mère J'ai vu cela sur stackoverflow:
    You can prevent redraw of child windows by setting WS_CLIPCHILDREN to parent window, or by invalidating redraw with the use of RedrawWindow function with RDW_NOCHILDREN flag

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Problème de rafraichissement de ma fenêtre
    Par kafiristanica dans le forum AWT/Swing
    Réponses: 2
    Dernier message: 13/05/2012, 16h17
  2. Fenêtre noire, problème de rafraichissement.
    Par Froyok dans le forum Ogre
    Réponses: 6
    Dernier message: 12/10/2009, 20h26
  3. Réponses: 16
    Dernier message: 18/03/2007, 13h30
  4. [JTree]problème de rafraichissement
    Par peppena dans le forum Composants
    Réponses: 9
    Dernier message: 20/01/2004, 14h06
  5. Toujours un problème de rafraichissement de DBGrid
    Par tripper.dim dans le forum C++Builder
    Réponses: 4
    Dernier message: 09/12/2002, 13h15

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo