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

Macros et VBA Excel Discussion :

Fermeture intempestive d'une macro suite à la fermeture manuelle d'un classeur


Sujet :

Macros et VBA Excel

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre averti
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    Avril 2007
    Messages
    29
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement matériel électronique

    Informations forums :
    Inscription : Avril 2007
    Messages : 29
    Par défaut Fermeture intempestive d'une macro suite à la fermeture manuelle d'un classeur
    Bonjour,

    J'utilise une macro qui ouvre un classeur et attend sa fermeture à l'aide d'une boucle.
    Lorsque je ferme manuellement ce classeur, la macro s'arrête totalement sans exécuter les instructions suivantes.
    Voici la macro :

    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
    Sub TestFermetureManuelleClasseurOuvertParProgramme()
    Workbooks.Open Filename:="c:\Liste.xlsx"
    Debug.Print "Position 1"
    Do
       DoEvents
       For i = 1 To Workbooks.Count
          If Workbooks(i).Name = "Liste.xlsx" Then
             Exit For
          End If
       Next i
       If i > Workbooks.Count Then
          Debug.Print "position 2"
          Exit Do
       End If
    Loop
    Debug.Print "position 3"
    Stop
    End Sub
    Lors de l'exécution, seul le texte "position 1" est écrit. Tout le reste est ignoré à la fermeture manuelle de Liste.xlsx.

  2. #2
    Expert éminent Avatar de Menhir
    Homme Profil pro
    Ingénieur
    Inscrit en
    Juin 2007
    Messages
    16 037
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Finistère (Bretagne)

    Informations professionnelles :
    Activité : Ingénieur
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2007
    Messages : 16 037
    Par défaut
    Citation Envoyé par nehoc Voir le message
    J'utilise une macro qui ouvre un classeur et attend sa fermeture à l'aide d'une boucle.
    Lorsque je ferme manuellement ce classeur, la macro s'arrête totalement sans exécuter les instructions suivantes.
    Je ne pense pas qu'elle s'arrête : elle tourne dans le vide.

    Si j'ai bien compris, elle ouvre le fichier Liste.
    Ensuite, elle suspend son exécution et tu fermes toi-même le fichier Liste.
    Alors que le fichier est fermé, tu lance une boucle sur tous les Workbook pour voir si l'un d'eux s'appelle Liste. Comme tu as fermé ce fichier, le If ne se déclenchera pas.
    A la fin de cette boucle, la variable i est égale au nombre de Workbook ouverts. Donc, le test vérifiant s'il est STRICTEMENT supérieur au nombre de Workbook ne se déclenchera pas non plus.
    On arrive en fin de boucle LOOP qui, n'ayant pas de condition de sortie, repart à son début pour redéclencher le DoEnvents.

    Et ainsi de suite à l'infini.

    Place un Debug.Print juste après ton Do, tu pourras vérifier ça.

    Autre remarque, le Stop est inutile (surtout placé en fin de code).

  3. #3
    Membre averti
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    Avril 2007
    Messages
    29
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement matériel électronique

    Informations forums :
    Inscription : Avril 2007
    Messages : 29
    Par défaut
    Bonsoir Menhir,

    Merci pour ta réponse.

    En fait le déroulement est le suivant :

    - la macro ouvre le fichier et entre dans la boucle d'attente de la fermeture

    - dans la boucle, l'instruction DoEvents permet au système de répondre à des actions externes à la macro et donc à l'opérateur de consulter le fichier puis de le fermer (il n'y est fait aucune modification et la fermeture se fait donc sans enregistrement via la petite croix)

    - à cet instant, la boucle For ne détecte plus le fichier et s'exécute totalement.

    - le test qui suit, détectant que le fichier n'est plus actif, fait sortir de la boucle d'attente par 'Exit Do' (i est alors égal au nombre de workbooks +1)

    - lorsque je place un debug.print après Do ou dans la boucle For, il s'exécute en permanence. La macro continue donc à tourner tant que le fichier n'a pas été fermé.

    - le fait que 'position 2' ne s'écrit pas conduit à penser que la macro ne tolère pas la perte du contrôle du fichier et se plante pile à l'instant de la fermeture, ce qui ne permet même pas à la boucle de s'exécuter une dernière fois.

    - Les stops permettent simplement de savoir comment s'est terminée la macro. Ils confirment le point précédent puisqu'il n'y a pas d'arrêt.


    PS :
    je viens de faire un essai de fermeture du fichier via le menu "Fichier/Fermer" : au premier essai, il ne se passe rien. Par contre, au second, le fichier se ferme et la boucle se termine normalement en s'arrêtant sur tous les Stop.

  4. #4
    Expert éminent Avatar de Menhir
    Homme Profil pro
    Ingénieur
    Inscrit en
    Juin 2007
    Messages
    16 037
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Finistère (Bretagne)

    Informations professionnelles :
    Activité : Ingénieur
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2007
    Messages : 16 037
    Par défaut
    Citation Envoyé par nehoc Voir le message
    - à cet instant, la boucle For ne détecte plus le fichier et s'exécute totalement.
    - le test qui suit, détectant que le fichier n'est plus actif, fait sortir de la boucle d'attente par 'Exit Do' (i est alors égal au nombre de workbooks +1)
    Non puisque le DoEvents se trouve avant le For.
    Le Workbooks.Count ne change pas entre le Next et le If, et comme à la fin de la boucle For, i = Workbooks.Count, il ne peut pas être ">" la ligne d'après.

    Je le répète, place un Debug.Print juste après ton Do, tu pourras vérifier ça.
    Ou bien place après le Next un
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Debug.Print i, Workbooks.Count
    - Les stops permettent simplement de savoir comment s'est terminée la macro. Ils confirment le point précédent puisqu'il n'y a pas d'arrêt.
    L'instruction Stop est un archaïsme bien pratique sur les Basics d'il y a 20 ans mais dans le VBA, il est possible de placer/retirer des points d'arrêt d'un seul clic sans toucher au code.

  5. #5
    Membre averti
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    Avril 2007
    Messages
    29
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement matériel électronique

    Informations forums :
    Inscription : Avril 2007
    Messages : 29
    Par défaut
    Bonjour,

    - Le code comportait l'indentation mais elle a disparu lors du collage.

    - Workbooks.count représente à tout moment exactement le nombre de classeurs ouverts mais celui-ci n'a strictement aucune importance.

    Le classeur Liste.xlsx, tant qu'il est ouvert est en permanence dans la collection workbooks et sa présence est détectée dans la boucle For à chaque passage et peu importent le nombre et l'ordre des classeurs. Elle se termine donc systématiquement avec i <= Workbooks.count. Le test If qui suit est négatif et la boucle redémarre.

    - Dès que le classeur Liste.xlsx est fermé, celui-ci doit disparaître de la collection. C'est là qu'il y a un problème :

    - la fermeture via la croix, trop brutale, n'interagit pas avec la macro qui considère qu'il est toujours présent : le test "If Workbooks(i).Name = "Liste.xlsx" Then" la plante car elle ne retrouve pas le fichier pour récupérer son nom.

    - par contre, comme indiqué dans le PS, la fermeture via le menu Fichier/Fermer est mieux contrôlée par Excel :
    . au premier essai, Excel détecte que le classeur est géré par la macro et refuse la fermeture.
    . au second essai de fermeture par le menu, prenant en compte la macro, il gère l'événement et positionne correctement tous les flags utiles.
    . La macro prend alors en compte l'événement et retire le classeur de sa collection : il n'y a donc plus de raison pour qu'elle se plante lors du test.

    - Le code qui suit s'exécute alors correctement (écritures et Stop). (J'ai mis des Stop car les points d'arrêts disparaissent au plantage).

    - une tentative de fermeture via le menu suivie d'une fermeture via la croix plante également la macro.

    - J'ai fait écrire l'heure juste après le Do : après le plantage, l'heure n'est plus affichée. Cela prouve que la macro est totalement arrêtée.

    En conclusion, je constate que la fermeture d'un classeur via la croix se fait sans tenir compte des processus en cours. Je ne sais pas, à moins d'ajouter une macro dans le classeur Liste.xlsx, comment éviter le problème.

  6. #6
    Membre Expert
    Profil pro
    Inscrit en
    Février 2007
    Messages
    2 266
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2007
    Messages : 2 266
    Par défaut
    Bonjour,

    Avec l'événement d'application WorkbookBeforeClose() ça a l'air d'être bon même avec la fermeture par la croix :
    Dans ThisWorkbook :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    Option Explicit
     
    Private WithEvents App As Application
     
    Private Sub Workbook_Open()
        'récupérer l'application Excel à l'ouverture
        Set App = Application
    End Sub
     
    Private Sub App_WorkbookBeforeClose(ByVal Wb As Workbook, Cancel As Boolean)
        If Wb.Name = "Liste.xlsx" Then MsgBox "Fichier Liste.xlsm Fermé !"
    End Sub
    Mettre le code, enregistrer le classeur, le fermer et le rouvrir pour que les événements Application soient actifs.
    eric

  7. #7
    Inactif  

    Homme Profil pro
    Développeur .NET
    Inscrit en
    Janvier 2012
    Messages
    4 903
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 69
    Localisation : Canada

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : Finance

    Informations forums :
    Inscription : Janvier 2012
    Messages : 4 903
    Billets dans le blog
    36
    Par défaut
    Citation Envoyé par nehoc Voir le message
    Bonjour,

    En conclusion, je constate que la fermeture d'un classeur via la croix se fait sans tenir compte des processus en cours. Je ne sais pas, à moins d'ajouter une macro dans le classeur Liste.xlsx, comment éviter le problème.
    C'est exactement ce que je t'ai dit:

    Personnellement j'utiliserais un des événement de la fermeture du classeur Liste pour lancer le signal que le classeur est fermé ou presque, et j'utiliserais ce signal-là pour ordonner la fin de la macro.

    - Dès que le classeur Liste.xlsx est fermé, celui-ci doit disparaître de la collection. C'est là qu'il y a un problème :

    - la fermeture via la croix, trop brutale, n'interagit pas avec la macro qui considère qu'il est toujours présent : le test "If Workbooks(i).Name = "Liste.xlsx" Then" la plante car elle ne retrouve pas le fichier pour récupérer son nom.

    - par contre, comme indiqué dans le PS, la fermeture via le menu Fichier/Fermer est mieux contrôlée par Excel :
    . au premier essai, Excel détecte que le classeur est géré par la macro et refuse la fermeture.
    . au second essai de fermeture par le menu, prenant en compte la macro, il gère l'événement et positionne correctement tous les flags utiles.
    . La macro prend alors en compte l'événement et retire le classeur de sa collection : il n'y a donc plus de raison pour qu'elle se plante lors du test.
    C'est donc beau la certitude. As-tu au moins exécuté ta macro au pas-à-pas pour vérifier ce qui se passe si tu fermes ton classeur Dans la boucle For et hors de la boucle For; juste pour voir ce qui se passe réellement et s'il se passe la même chose ?

    Et puis, si, au pas à pas, la macro se comporte toujours comme tu dis qu'elle fonctionne, ou plutôt comme elle doit fonctionner, il y a d'énormes chances que ce soit que ton pas-à-pas soit assez lent pour permettre à VBA, Excel et Windows de traiter tous les événements. Sinon, tu vas devoir te questionner sérieusement pourquoi le comportement diffère selon la technique de fermeture.

    Ce que j'ai dit pour la mise à jour de WorkBooks.count s'applique également aux collections et à bien d'autres choses.

    De plus n'oublie pas que Excel est mono-tâche et tu n'as pas le contrôle sur l'ordre des opérations qu'Excel, et même Windows, vont privilégier ou appliquer. Tu ne peux absolument pas être certain que WorkBooks.count est mis à jour instantanément, ou assez tôt pour ne pas causer d'erreurs dans la suite des opérations.
    Et puis comment es-tu vraiment "certain" que VBA, Excel et Windows sont vraiment capables de gérer tous les événements créés par ta macro et tous les événements d'Excel et externes à Excel sans en oublier en chemin ?

    Mais bon, puisque tu es certain que tout fonctionnes comme tu penses (veux ou espère), à quoi bon continuer à lire et à répondre à ta prose ?

  8. #8
    Inactif  

    Homme Profil pro
    Développeur .NET
    Inscrit en
    Janvier 2012
    Messages
    4 903
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 69
    Localisation : Canada

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : Finance

    Informations forums :
    Inscription : Janvier 2012
    Messages : 4 903
    Billets dans le blog
    36
    Par défaut
    Bonjour,

    Tout d'abord, désolé d'avoir douté

    Citation Envoyé par nehoc Voir le message
    Bonjour,

    - Le code comportait l'indentation mais elle a disparu lors du collage.
    http://club.developpez.com/aidenouve...es/Balises.gif

  9. #9
    Inactif  

    Homme Profil pro
    Développeur .NET
    Inscrit en
    Janvier 2012
    Messages
    4 903
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 69
    Localisation : Canada

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : Finance

    Informations forums :
    Inscription : Janvier 2012
    Messages : 4 903
    Billets dans le blog
    36
    Par défaut
    Bonjour,

    Avec la macro indentée, cela va un peu mieux.

    Citation Envoyé par nehoc Voir le message
    Bonjour,

    J'utilise une macro qui ouvre un classeur et attend sa fermeture à l'aide d'une boucle.
    Lorsque je ferme manuellement ce classeur, la macro s'arrête totalement sans exécuter les instructions suivantes.
    Voici la macro :

    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
    Sub TestFermetureManuelleClasseurOuvertParProgramme()
    Workbooks.Open Filename:="c:\Liste.xlsx"
    Debug.Print "Position 1"
    Do
       DoEvents
       For i = 1 To Workbooks.Count
          If Workbooks(i).Name = "Liste.xlsx" Then
             Exit For
          End If
       Next i
       If i > Workbooks.Count Then
          Debug.Print "position 2"
          Exit Do
       End If
    Loop
    Debug.Print "position 3"
    Stop
    End Sub
    Lors de l'exécution, seul le texte "position 1" est écrit. Tout le reste est ignoré à la fermeture manuelle de Liste.xlsx.
    Analysons un peu en détails:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     For i = 1 To Workbooks.Count
          If Workbooks(i).Name = "Liste.xlsx" Then
             Exit For
          End If
       Next i
    Ton Workbooks.count comprend tous les classeurs ouverts. Cela comprend le "Classeur de macros personnelles", toutes les macros complémentaires fournies dans leurs propres classeurs, ton classeur qui contient cette macro-ci, ton classeur Liste.xlsx et tout autre classeur non lié à cette opération qui serait déjà ouvert. C'est donc dire que le nombre peut être très variable, mais bon, tu n'as probablement pas le choix.

    Mais ton exit for entraîne certains autres risques. Dépendant de l'ordre des classeurs et de la manière dont VBA a ordonné tout cela, ta boucle peut se terminer, sans aucune garantie de la valeur que i peut avoir à ce moment-là.

    Donc, quand tu es rendu là:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    If i > Workbooks.Count Then
    tu n'as aucune garantie de la valeur de i. Si ton classeur Liste.xlsx est le deuxième à passer la boucle, ton i vaut 2, etc...

    De plus n'oublie pas que Excel est mono-tâche et tu n'as pas le contrôle sur l'ordre des opérations qu'Excel, et même Windows, vont privilégier ou appliquer. Tu ne peux absolument pas être certain que WorkBooks.count est mis à jour instantanément, ou assez tôt pour ne pas causer d'erreurs dans la suite des opérations.

    Et puis, surtout : Es-tu vraiment certain que ta boucle du début est exécutée quand tu fermes ton classeur ?

    À mon humble avis, ta méthode pour tester si ton classeur est fermé est pour le moins déficiente, ou au pire inapplicable. Personnellement j'utiliserais un des événement de la fermeture du classeur Liste pour lancer le signal que le classeur est fermé ou presque, et j'utiliserais ce signal-là pour ordonner la fin de la macro.

    P.S. @menhir Je partage (presque) entièrement ton analyse. Mon bémol est ici, même si cela ne change fondamentalement rien à ce que tu dis:

    A la fin de cette boucle, la variable i est égale au nombre de Workbook ouverts.
    Après avoir suivi au pas-à-pas nombre de boucles for, même si je ne peux pas jurer que c'est toujours cela, le compteur est incrémenté par le next, mais le test est fait par le For. Donc quand For dit que le boucle est complète, le compteur vaut en fait le "To" + 1; s'il n'y a pas de exit for qui a sifflé plus tôt la fin de la récréation. Donc, si sa boucle te termine avec un count = 5, son i vaut 6. Mais comme je dis plus haut, le exit for peut faire que son i est variable et que le WorkBooks.count peut ne pas être exact. Ces machines-là sont rapides mais elles ont tendance à être têtes de cochon.

Discussions similaires

  1. Eviter la fermeture intempestive d'une base ACCESS
    Par Claude40 dans le forum Access
    Réponses: 5
    Dernier message: 31/03/2015, 23h55
  2. [XL-2007] Lancer une macro suite à un changement de valeur de cellule
    Par jnauche dans le forum Excel
    Réponses: 1
    Dernier message: 21/03/2014, 17h38
  3. [PPT-2003] Interrompre une macro jusqu'à la fermeture d'une boite de dialogue
    Par thilamb dans le forum VBA PowerPoint
    Réponses: 1
    Dernier message: 29/11/2011, 08h47
  4. Suppression d'une macro enregistrée à chaque fermeture du classeur
    Par Leila59 dans le forum Macros et VBA Excel
    Réponses: 6
    Dernier message: 18/08/2008, 17h07
  5. declencher une macro suite a une modification d une feuille Excel
    Par chamus dans le forum Macros et VBA Excel
    Réponses: 15
    Dernier message: 11/01/2007, 09h50

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