|
Publicité ' | ||||||||||||||||||||||||
|
|
#1 | ||||||
|
Membre Expert
![]() ![]() Etienne PailleretDéveloppeur VBA Inscription : mars 2004 Messages : 748 ![]() |
>>>> Merci de noter toutes remarques concernant ce cours dans le sujet parallèle : [Cours papyturbo]Commentaires, remarques et suggestions
------------------------------------------------------------------------- Ce cours est la suite - du [Cours pt-01][Débutants]Analyse structure base de données simple et - du [Cours pt-02][Débutants]Requête avec plusieurs sommes - et représente une "pause" dans le déroulement du [Cours pt-03]turbo-formulaire (les bases), pause destinée à défricher les bases du débogage avant de retourner finir notre formulaire. Il commence donc par une réponse aux questions posées par Serge57, dans la réponse #10 du cours précédent (le 03), et nous travaillons à partir du dernier .mdb : SuiviAffaire 2006-08-17.zip (89,0 ko) ------------------------------------------------------------------------- Zzzzzzap!, zoooing ! tiou tiou tiou tiou, sortez vos lasers quantiques et vos tromblons zappeurs, enfilez le costard de Blade Runner, on part à la chasse aux bugs , en espérant que ce ne soient pas eux qui nous flinguent les premiers. ![]() D'abord, se référer au débat [Conseils] Comment retrouver un problème , qui traite de ce sujet assez à fond. Il contient tous les principaux aspects du débogage et mérite d'être relu par tous. Ici, nous détaillerons surtout les étapes de base pour débutants. ---------------- Je reprends le bug déjà signalé et apparemment corrigé dans la réponse #9. La 1ère étape consiste à identifier le bug. Si un message d'erreur s'affiche, facile. Là, ce n'était le cas qu'à condition de faire des tests. En l'occurence, réduire le formulaire à sa taille la plus petite, à partir du coin inférieur droit (mais également : le passer en plein écran, puis en réduction, etc.) La 2ème étape, c'est le travail du beta testeur qui doit décrire aux développeurs "Comment reproduire le bug". En tant que développeurs, on doit au moins savoir faire cela, et ça a consisté en (dans la réponse #9 du cours 03) : Citation:
La 3ème étape consiste à comprendre ce qu'il se passe et, lors de l'affichage du message, j'ai une boîte de dialogue avec le message et 2 boutons actifs : Fin (on arrête tout, sans commentaire) ou Débogage (clic) Le bouton Débogage m'amène sur une ligne de code jaune, la ligne qui provoque l'erreur : Code :
Me.Section(acDetail).Height = NewHeight Pour ça, les techniques de base sont : - laisser la souris au dessus du nom d'une variable, comme 'NewHeight' : sa valeur s'affiche dans une bulle. Ça ne marche pas toujours pour une expression plus complexe, comme 'Me.Section(acDetail).Height (si la souris est au dessus de 'acDetail', la bulle affiche 'acDetail = 0', ce qui ne nous intéresse pas). Donc : - sélectionner l'expression et appuyer sur Maj+F9 : la valeur s'affiche dans une boîte de dialogue. Troisième méthode : - ouvrir la fenêtre de débogage avec Ctrl+G, et, dans la fenêtre d'exécution qui s'ouvre sous le code, taper un '?' suivi d'un espace et de la valeur, le nom de la variable ou l'expression à évaluer : Maintenant, je sais que - ma section détail, avant le Form_resize, mesurait 105 twips en hauteur, - la variable NewHeight est négative (-5). D'où vient cette NewHeight négative ? Elle est calculée au dessus, en gros : Code :
NewHeight = Me.InsideHeight - Me.Section(acHeader).Height Ce qui veut juste dire que je suis remonté tellement vite que InsideHeight (la fenêtre) est plus petit que la section acHeader (l'en-tête du formulaire). 4ème étape : trouver la solution. Il paraît ici évident qu'on ne peut pas demander à Access de dessiner une section Détails dont la hauteur serait négative ! D'où, solution : Code :
5ème étape : documenter le probème. Et ça devient : Code :
Trop de commentaires tueraient les commentaires ? Peut être, mais il en faut beaucoup pour que ce soit trop et au minimum, il faut signaler toute correction de bug ou de message d'erreur, comme ici. --------------------- Voilà, en 5 étapes, comment le premier message d'erreur a été "corrigé". Et tu me signales qu'il est encore là ? Citation:
![]() Donc, même méthode -> reste plus qu'à trouver la solution appropriée ? |
||||||
|
|
00
|
|
|
#2 | ||||
|
Membre du Club
![]() Inscription : mai 2006 Messages : 79 ![]() |
Citation:
Code :
Citation:
|
||||
|
|
00
|
|
|
#3 | ||
|
Membre Expert
![]() ![]() Etienne PailleretDéveloppeur VBA Inscription : mars 2004 Messages : 748 ![]() |
Citation:
Que demande le peuple ? C'est top cool, et c'est ce qu'il faut maintenant, à chaque routine, quelle qu'elle soit. Stop : pas vrai. Pas top cool. Et l'étape 5 ? In-dis-pen-sa-ble : noter un commentaire aussi concis que possible, mais clair ! Puisque tu commences à être rôdé, continuons avec un bug un poil plus difficile. Dans la réponse #8 du cours pt03, tu as écrit : Citation:
- il faut peut être compléter cette première étape, en identifiant clairement le problème ("qu'est-ce qu'il fait ?" et "qu'est-ce qu'il devrait faire" ? ou bien "qu'est-ce qu'il fait qui ne va pas ?" - étape 2 : comment reproduire le bug ? et puis : 3, 4 et 5, bien sûr, mais il risque d'y avoir des pièges. Comme quoi, si quelque chose semble bizarre, faut fouiner ou, au minimum, le noter pour plus tard, mais jamais laisser passer, sinon, ça ne peut qu'empirer après... Coups de pouce (je conseille à chacun, dont Serge, de ne pas trop se faire aider, ça pert tout son fun) : 1- pour arrêter le code qui pose problème, pouvoir inspecter les valeurs des variables et autres paramètres, et détecter la ou les instructions qui font que ça déc..., il va falloir, soit - appuyer sur F9 (ou menu Débogage > Basculer le point d'arrêt, dans l'IDE (espace de travail en VBA : I pour quèque chose genre "Interactive" ? + "Development Environment") - écrire un Stop dans le code, éventuellement suivi de ":", si tu le mets devant une instruction existante. Chacun son usage : Le F9 (point d'arrêt) est temporaire. Le Stop peut être enregistré avec le code (jusqu'à ce que le bug soit résolu, par exemple). 2- s'il se passe des choses bizarres, ça vaudra le coup d'appuyer sur Ctrl+L (ou menu Affichage > Pile des appels). On en reparlera, mais j'attends tes questions / observations... Bien entendu, les points d'arrêt et la pile des appels sont documentés dans l'aide d'access. |
||
|
|
00
|
|
|
#4 | |
|
Membre du Club
![]() Inscription : mai 2006 Messages : 79 ![]() |
Citation:
1ère étape - Identification du bug -– un ascenseur apparaît verticalement quand je rajoute une ligne de menu access supplémentaire. 2éme étape – reproduction du bug – dans application « SuiviAffaire » ouvrir le formulaire 10-100 Affaires, si dans la barre de menu il y a plus de deux lignes « de boites outils » un ascenseur vertical apparaît. 3éme étape – comprendre ce qui se passe – Il n’y a pas de message d’erreur. Je suppose qu’il y a un petit hic sur la valeur de la variable « InsideHeight » retournée par access. J’ai remarqué qu’a chaque affichage d'un nouveau menu, la variable prend une autre valeur. Mais je n’ai pas trouvé de relation cohérante pour faire un "IF" …. Suis-je sur la bonne piste ? ------- Fichiers attachés : reponse-4.doc (158,0 ko) ------- |
|
|
|
00
|
|
|
#5 | ||||||||||
|
Membre Expert
![]() ![]() Etienne PailleretDéveloppeur VBA Inscription : mars 2004 Messages : 748 ![]() |
Bon pour l'identification du bug, et on peut le reproduire sans problème (étape 2). Sauf que j'aurais précisé : il faut que le formulaire Affaires soit en 'plein écran' pour que l'ascenseur apparaisse à droite (ça tombe bien, il se met en plein écran au démarrage
Par contre, attention : la phrase Citation:
Citation:
- quel que soit le nombre de barres d'outils, - si j'en supprime une (menu Affichage > Barres d'outils, ou clic droit dans l'espace des barres d'outils) : aucun problème, pas d'ascenseur, - si j'en ajoute une : apparition de l'ascenseur, ce qui permet, en le faisant glisser vers le bas, de voir une bande orange en dessous du sous-formulaire ssfAffaires. De plus, à l'oeil nu, cette bande semble avoir à peu près la hauteur d'une barre d'outils. Citation:
. Donc, ça vaut le coup d'y passer un moment, en détail.Donc tu as dû - ouvrir le code (Alt+F11) de l'évènement Form_Resize() - cliquer sur la 1ère ligne (ou autre ?) et appuyer sur F9 pour insérer un 'point d'arrêt' ou bien - taper l'instruction Stop - revenir vers le formulaire, - faire le test (afficher une nouvelle barre d'outils), - chercher la valeur de InsideHeight et autres variables, lorsque l'exécution du code s'arrête sur le point d'arrêt ou sur le Stop. Citation:
Je pense qu'elle change, en fonction de la taille allouée au formulaire. Le bon sens nous dit : - la fenêtre d'Access contient tout : barre de titre + menu (1 bande) + toutes les barres d'outils (1 bande chacune) + le formulaire, qui occupe "le reste". - quand on ajoute une barre d'outils ou de menu, on réduit l'espace vertical alloué au formulaire -> à vérifier de test en test. Je comprends que tu aies du mal à suivre ce qu'il se passe. Suite des outils de débogage : ------------- Une fois l'exécution du code arrêtée par un point d'arrêt ou une instruction Stop, on peut : - (rappel) inspecter chaque valeur avec la souris, ou avec Shift+F9, - appuyer sur F8 pour exécuter chaque instruction en pas à pas. Normalement ! Ce que je constate aussi : - si j'ai mis l'arrêt sur la 1ère ligne : Code :
Ce qui est bien. Mais, si j'appuie encore sur F8 pour exécuter cette instruction, la ligne jaune (prochaine à exécuter) remonte en arrière !!! L'exécution repasse sur la 1ère ligne ! Non, il n'y a pas vraiment de bug dans Access. Le problème ici, c'est que, à chaque fois qu'on exécute cette 2ème ligne de code, Access considère qu'on a changé la taille du formulaire et relance l'exécution de l'évènement Form_Resize. Ce qu'on peut constater en appuyant sur Ctrl+L : affichage de la pile des appels. ![]() Ce qu'on voit c'est que, à chaque fois qu'on exécute une ligne de code, la 'Fenêtre de débogage' relance l'exécution de l'évènement ! Résultat : les évènement Form_Resize sont parmi les pires à déboguer , et on ne pourra pas utiliser F8 (faire du pas à pas) dans un évènement Form_Resize().On va devoir utiliser un autre outil : Debug.Print L'instruction Debug.Print (sur une ligne de code, il suffit de taper 'Debug.?') va afficher ce qu'on veut, dans la fenêtre d'exécution. Par exemple : Code :
Comme l'instruction est tout en haut de la subroutine, elle affiche la valeur avant d'exécuter notre code. Mais on pourrait aussi en mettre d'autres après, plus bas, pour avoir les résultats. Si je rééxécute le test (ajout / suppression d'une barre d'outils), je vois des valeurs défiler dans la fenêtre d'exécution, comme, par exemple : Code :
- sans mettre aucun Stop ni point d'arrêt, - savoir la hauteur de chaque objet (NewHeight + Section(acDetail) + ssfAffaires), - aussi bien avant d'exécuter le code de l'évènement Form_Resize qu'après, - à chaque opération (agandissement, réduction...). Est-ce que, avec ces outils, le problème commence à être plus précis ? ou bien tu nages encore plus ????
|
||||||||||
|
|
00
|
|
|
#6 | |||||
|
Membre du Club
![]() Inscription : mai 2006 Messages : 79 ![]() |
Citation:
Résultat obtenu des variables. 1er lancement Code :
Code :
Donc la fenêtre contenant le sous formulaire « ssfAffaires » se corrige. Il reste le formulaire contenant, le formulaire « Affaires ». Il faudrait réajuster la hauteur du formulaire ? Je penche pour la variable windowheight. Suis je sur le bon chemin ? |
|||||
|
|
00
|
|
|
#7 |
|
Membre Expert
![]() ![]() Etienne PailleretDéveloppeur VBA Inscription : mars 2004 Messages : 748 ![]() |
On approche, on approche, mais c'est encore froid.
![]() J'aurais mis un peu plus d'indications, dans ces debug.print, concernant tout ce que nous essayons, avec plus ou moins de succès, de modifier. Les sections Header et Footer ne bougeront jamais, tes tests le confirment. Mais la section Détail, par contre, devrait être toujours égale à NewHeight, non ? Et le sous-formulaire aussi, à (2*.Top) près. Et, comme déjà indiqué, je ferais ces tests 2 fois à chaque resize : - avant nos opérations (au début de la routine), pour vérifier si on a la même valeur qu'au passage précédent, - après nos opérations, (juste avant le Exit) pour vérifier si les transformations sont bonnes ? Avec un mot ("avant" ou "après") dans chaque Debug.Print, pour qu'on sache. Petite astuce qui peut être utile pour mettre des commentaires dans la fenêtre d'exécution(par exemple noter, pour nous, si tu as ajouté ou si tu as masqué une barre d'outil, avant chaque série de mesure) : - si tu essayes d'aller à la ligne avec Enter, message d'erreur : Access essaye de compiler et exécuter la ligne. - tu peux aller à la ligne avec Ctrl+Enter sans afficher ce message d'erreur. Autre (voir la syntaxe de Debug.Print dans l'aide - F1) : - tu peux mettre autant de noms et de valeurs que tu veux sur la même ligne, en les séparant par des ";" - une ligne de commande Debug.Print qui se termine par un ";" ne va pas à la ligne. Donc, la prochaine commande continuera sur la même ligne. |
|
|
00
|
|
|
#8 | ||||||||||||
|
Membre du Club
![]() Inscription : mai 2006 Messages : 79 ![]() |
Citation:
Code :
Code :
Insertion d’une nouvelle barre de menu, résultat dans la fenêtre exécution est : Code :
Après plusieurs essais et lecture des variables, - dés que la hauteur du sous formulaire est modifiée, Access recalcule la hauteur du de la section détail. D’où le nouveau code : Code :
Revenons à nos étapes. 1 - Identification du bug réponse # 4 2 - reproduction du bug réponse #4 corrigé par Papy Turbo dans réponse #5 3 - Comprendre ce qui se passe Si l'on modifie la hauteur d'un sous formulaire (ou sous état), Access recalcul la hauteur de la section ou se trouve "ce sous formulaire." 4 - Trouver la solution Une fois la hauteur du sous formulaire imposer, il faut impose la hauteur de la section contenant le sous formulaire. (Tout en restant logique avec la variable "InsideHeight" 5 - documenter le problème d'où le nouveau code: Code :
Citation:
|
||||||||||||
|
|
00
|
|
|
#9 | ||||||||
|
Membre Expert
![]() ![]() Etienne PailleretDéveloppeur VBA Inscription : mars 2004 Messages : 748 ![]() |
![]() Ben voilà. Dans ce sens là, ça marche. ![]() Comme je suis vicieux et à l'intention de tous ceux qui meurent d'envie de se plonger dans le monde fascinant du Form_Resize(), je vais juste montrer ce que j'ai fait (c'est quasi identique à ce que tu as fait, juste une ligne de plus) , et les conclusions. L'ancien code, au complet, avec les Debug.Print : Code :
Il a la particularité se mettre (en option seulement, mais ça me plaît) tous les Debug.? dans la marge, à gauche, et plein d'autres options... Et le résultat, copié de la fenêtre d'exécution ou j'ai rajouté le type d'action (ajout ou suppression barre d'outils) : Code :
Lorsqu'on essaye de réduire la section acDetail, - elle contient un sous formulaire qui mesure encore 7551 twips (il n'a pas encore été réduit), - le ssf démarre à 57 twips (=.Top) du haut de la section. Access essaie en fait de le réduire, mais (bon sens ?), il ne peut pas le réduire plus que ce qu'il contient !!! ![]() Dans l'autre sens, en inversant les 2 lignes comme toi, on constate des choses intéressantes : Code :
Note : ne pas oublier la gomme, sur la barre d'outils de mzTools, pour nettoyer la fenêtre d'exécution Code :
Sur la ligne 2, - On n'a pas encore touché à la section acDetail, - on a agrandi le ssf de 7161 à 7551, - la section détail a été agrandie (par access) de 7275 à 7608 (soit toujours de quoi contenir le ssf et son .Top, 7608 = 7551 + 57) En conclusion, - quand on agrandit un contrôle à l'intérieur d'une section, Access agrandit la section pour contenir le tout, - quand on essaye de réduire la section, il limite la réduction à la taille des contrôles contenus. Maintenant, faut pas rêver en pensant que c'est fini ! ![]() C'est bien fini dans notre cas, mais, si j'ai insisté pour bien comprendre le mécanisme, c'est qu'un jour ou l'autre, tu vas tomber sur d'autres conteneurs de contrôles, qui ne se comporteront peut être pas comme la section acDetail !!! Et là, suffira de faire les mêmes tests, comprendre, solution, commentaire. Bon, ben on retourne à notre formulaire, dans le cours #03, nouvelle réponse #12. |
||||||||
|
|
00
|
|
|
#10 | ||||
|
Membre Expert
![]() ![]() Etienne PailleretDéveloppeur VBA Inscription : mars 2004 Messages : 748 ![]() |
Reprenons à partir de l'application 'mise à jour de données', jointe à ta réponse #22 du cours 05.
Le 1er problème vient du fait qu'on a une monstruosité dans le code de gestion d'erreur : Code :
![]() Ensuite, oublions comment cette énormité est arrivée là, un copier/glisser quelconque ? et reprenons la notion de base avec une La Palissade : les seules erreurs à trouver sont les erreurs non prévues. La bonne routine de gestion d'erreurs non prévues, dans notre cas, redevient donc : Code :
Je répète lourdement le but : on ne peut pas quitter cette routine sans passer par le Finally:, même (et surtout) s'il y a une erreur non prévue. - le Debug.? sera utile pour nous, pour retrouver le n° et description d'erreur pendant et surtout après le débogage, quand on en sera à la gestion d'erreur connue (identifiée, déboguée, à traiter). - les 3 choix sont utiles pour un utilisateur en face d'une erreur non prévue. Il peut : 1- parfois, corriger l'erreur : il faut introduire une disquette ou clé USB, ou se reconnecter au réseau... Ensuite, il clique sur Réessayer et ça devrait marcher. 2- sinon, il clique sur Ignorer. Ça peut surprendre, mais dans bon nombre de cas, ça marche quand même. 3- si la 1ère erreur en déclenche d'autres, comme une 'variable objet non définie'..., il clique sur Annuler. Puis il recommence le tout. - Enfin, le Stop va nous permettre, si Debugging est vrai (à faire dans appInit() et dans la fenêtre d'exécution : ThisApp.debugging = True) de cliquer sur Réessayer > l'exécution s'arrête au Stop > on reprend avec F8 qui va faire un Resume > on continue en pas à pas, toujours avec F8... À partir de là, faut se creuser le ciboulot pour bien comprendre - comment marche ce code ? - qu'est-ce qui cloche ? Déjà, tu as vu qu'il faut être très rigoureux avec tes tests et les méthodes de débogage d'une routine écrite par toi. Mais c'est beaucoup plus drôle de déboguer quelque chose qui a été écrit par quelqu'un d'autre ![]() Et si j'en juge par la constante mauvaise humeur des développeurs qui reprennent un code "bidouillé par je ne sais quel tocard-nulard", ça arrive tout le temps. Donc, c'est ton tour de me déboguer, pendant que je vais boire l'apéro. Je ne m'attends pas à ce que tu cherches des jours et des jours, pour apporter la solution toute crue. N'hésite pas à expliquer ce que tu as fait, les difficultés, ce que tu ne comprends pas... On va chercher ensemble (après l'apéro). |
||||
|
|
00
|
|
|
#11 | ||
|
Membre du Club
![]() Inscription : mai 2006 Messages : 79 ![]() |
Citation:
La ‘reconnections’ des tables utilisent les variables « OldBase » et « NewBase » Dans la ‘sub Reconnect’ la variable « OldPathName » me semble bien formaté (chemin + nom) alors que la variable « NewPathName » il manque le nom du fichier. Bonne approche ??? Citation:
|
||
|
|
00
|
|
|
#12 |
|
Membre Expert
![]() ![]() Etienne PailleretDéveloppeur VBA Inscription : mars 2004 Messages : 748 ![]() |
Bonne approche ?
- Tu n'es peut être pas loin, mais je ne sais pas si tu as la bonne méthode. En tout cas, tu n'es pas sûr de toi, donc il manque quelque chose. Sur la méthode : - tu examines ta routine, si les variables font bien leur boulot... Tu risques d'y passer des heures et des jours, à revérifier chaque variable, chaque algorythme, chaque calcul... Pour l'instant, ce que je cherche en premier, c'est : "quelle est la ligne qui provoque une erreur # ???, message "le nom de fichier est incorrect". Le n° d'erreur est généralement important. C'est par là qu'on l'identifie et qu'on va la traiter, si besoin. Donc, - si tu as bien suivi les corrections indiquées plus haut, tu dois pouvoir - relancer le programme, - obtenir l'erreur, - noter son n° et son message (merci, Debug.?) - t'arrêter sur la ligne : - Resume te ramène effectivement sur À partir de là, faut trouver la ligne "qui plante", seul moyen d'être sûr que le problème est bien là. |
|
|
00
|
|
|
#13 | |||
|
Membre du Club
![]() Inscription : mai 2006 Messages : 79 ![]() |
Citation:
La ligne qui pose problème se trouve dans la ‘Sub Reconnect ….’ Code :
Donc pour corriger: ancien code Nouveau code Des tests …. Et ça marche !!!! |
|||
|
|
00
|
|
|
#14 |
|
Membre Expert
![]() ![]() Etienne PailleretDéveloppeur VBA Inscription : mars 2004 Messages : 748 ![]() |
Ben, bravo !
![]() Tu as - trouvé la ligne qui plante, - du coup, tu es sûr du problème à résoudre, et tu as la solution sans trop de difficulté. Mais, ce qui m'intéresse, plus encore que la solution, c'est la méthode. Autrement dit, comment tu as fait pour t'arrêter sur la ligne qui plante, bien qu'elle soit dans une routine qui n'a aucun contrôle d'erreur (pas de On Error Goto ...), laquelle est appelée par une routine qui en a... ? On est là pour connaître à fond tous les outils de débogage. Autrement dit, savoir où se trouvent les interrupteurs qui permettent d'allumer et de ne pas rater une marche quand on descend à la cave fouiller dans le charbon..., si tu vois ce que je veux dire. Donc, n'hésite pas à raconter les marches ratées, les outils, les options, les paramètres que tu as changés pour y arriver. |
|
|
00
|
|
|
#15 | |
|
Membre du Club
![]() Inscription : mai 2006 Messages : 79 ![]() |
Citation:
1- dans l’éditeur VBA, menu ‘Options’ - ‘Général’ – ‘Récupération de erreurs’ – coché la case ‘Arrêt sur toutes les erreurs’ 2- Relancer l’application. 3- Retourne l’erreur 3044 (chemin non valide pour le fichier) sur la ligne ‘Set ConnectTest = db.OpenRecordset(td.Name)’ 4- Remettre dans l’éditeur VBA ‘Arrêt sur les erreures non gérées’ 5- Relancer l’application‘ 6- Dérouler la routine pas à pas « touche F8 », lors du choix du fichier et de l’essai de reconnections (Reconnect OldBase, NewBase), dans la variable ‘NewBase’, le chemin est bien formaté, mais il manque le nom du fichier. 7- Conclusion ‘Reconnect OldBase, NewBase & Filename’ Dure d’expliquer la démarche !!
|
|
|
|
00
|
|
|
#16 | |||||||||||
|
Membre Expert
![]() ![]() Etienne PailleretDéveloppeur VBA Inscription : mars 2004 Messages : 748 ![]() |
Citation:
![]() Ta démarche est bonne, puisque tu as trouvé. Mais j'ai insisté parce que mon petit doigt me disait que tu avais eu du mal à trouver ça, et donc, que quelques concepts de base n'étaient pas encore tout à fait clairs. Voici ma démarche, quasi identique, pour trouver la ligne qui donne l'erreur : 1- déjà exposé en détail, arrêt, dans notre code de gestion d'erreur, sur le Stop, 2- F8 sur le Resume, ce qui me ramène sur la ligne Reconnect OldBase, NewBase 3- toujours F8 pour entrer dans la Sub Reconnect(...) 4- Pause : dès que je constate que cette Sub n'a aucun contrôle d'erreur (pas de 'On Error Goto ...'), c'est là que j'ouvre le menu Outils > Options > Général pour choisir Arrêt sur toutes les erreurs, 5- puis, pour gagner du temps, F5 (exécution complète) 6- l'exécution s'arrête sur .RefreshLink, avec le message d'erreur 'Nom de fichier incorrect' > clic sur débogage. À partir de là, le problème est quasiment résolu : je suis sur l'erreur proprement dite, RefreshLink plante, donc le nouveau chemin + nom de fichier ne doit pas être correct... Maintenant, tâchons de clarifier tout cela : - quand utilise t'on Arrêt sur erreurs non gérées ou bien Arrêt sur toutes les erreurs ? - À quoi peut bien servir l'option Arrêt dans le module de classe ? Il y a quelques notions de base que les 2 exercices ci-dessous, et les centaines de variations que je te conseille de pousser plus loin (ajouter plusieurs routines qui s'appellent l'une l'autre, sur plus de niveaux ; mettre du contrôle d'erreur à des niveaux variables ; etc.) doivent rendre absolument évidentes pour tout développeur : L'ensemble de notre code se divise en 2 catégories : erreurs gérées ou erreurs non gérées. Le code avec erreurs gérées - commence avec un 'On Error Goto ...' ou bien 'On Error Resume Next' - se termine avec un 'On Error Goto 0', ou bien avec la fin de la routine (End/Exit Sub/Function...) - inclut tout le code contenu dans les subroutines appelées, à condition qu'il n'y ait pas d'autre gestion d'erreur dans la routine appelée. Sinon, c'est cette gestion d'erreur locale qui prend le pas, jusqu'à ce qu'on remonte à l'appelant ou qu'on rencotre un 'On Error Goto 0'. Tant qu'aucune erreur n'est gérée, c'est facile pour nous : l'exécution s'arrête en affichant la boîte avec les options 'fin / débogage', ce qui nous permet de corriger (ou gérer) l'erreur. Si une erreur se produit - dans un bloc avec gestion d'erreur : l'exécution continue là où nous l'avons spécifié avec le On Error..., - dans un bloc sans gestion d'erreur, --- soit la routine où se produit l'erreur est appelée depuis une autre -> VBA va 'remonter' jusqu'à la gestion d'erreur spécifiée dans l'appelant. --- soit la routine n'est appelée par aucune autre : l'exécution s'arrête en affichant la boîte avec les options 'fin / débogage'. --- soit il n'y a aucune gestion d'erreur à aucun niveau : l'exécution s'arrête également, au niveau d'appel le plus bas, sur la ligne qui a provoqué l'erreur, en affichant la boîte avec les options 'fin / débogage'. Exercices : lancer chaque exercice en cliquant dans le code + F5. Routine simple, pas de contrôle d'erreur Code :
Arrêt sur : - toutes les erreurs - dans module de classe - erreurs non gérées Ici, quelle que soit l'option, l'exécution s'arrête sur la ligne d'erreur et affiche la boîte avec les options 'fin / débogage'. Routine simple, avec contrôle d'erreur Code :
- toutes les erreurs : boîte de débogage (Fin/débogage), impossible de continuer avec F8 - dans module de classe : pas d'arrêt, contrôle d'erreur actif -> notre MsgBox s'affiche - erreurs non gérées : idem module de classe. 2 niveaux de routines, contrôle d'erreur au niveau appelant (niveau 0) : Code :
- toutes les erreurs : arrêt dans TestLevel1, sur l'erreur impossible d'aller + loin avec F8 - dans module de classe : pas d'arrêt, contrôle d'erreur actif au niveau 0 -> notre MsgBox s'affiche L'instruction : Debug.Print "Level 1 - Instruction 2" ne sera jamais exécutée. - erreurs non gérées : idem module de classe. Refaire le même, sans aucune gestion d'erreur, seulement au niveau bas... Appel d'une méthode, dans un module de classe Toujours dans Module1, l'appelant devient : Code :
Code :
- toutes les erreurs : arrêt dans Method1, sur l'erreur impossible d'aller + loin avec F8 - dans module de classe : idem toutes les erreurs : l'exécution s'arrête directement sur l'erreur. - erreurs non gérées : pas d'arrêt, contrôle d'erreur actif -> notre MsgBox s'affiche L'instruction : Debug.Print "Class module - Instruction 2" ne sera jamais exécutée. Quel est l'intérêt de cette distinction ? Pourquoi s'arrêter spécifiquement dans les modules de classe et non pas dans les modules standards ??? Il m'a fallu quelques années pour y comprendre quelque chose. Pour cela, il faut penser à la réutilisation du code. Je vais simplifier bien sûr, en répondant à la question : Quand utilise t'on des modules de classe ? et en ne donnant que 2 réponses : 1- pour créer des objets métier : ces objets peuvent être réutilisés dans plusieurs applications complémentaires les unes des autres. 2- pour se constituer une bibliothèque (Library) de code réutilisable dans toutes (ou au moins dans plusieurs) de nos applications. Le 2ème cas est plus subtil, parce qu'il n'y aura pas QUE des modules de classe dans une librairie complète. Il y aura des modules de classe et des routines simples, dans des modules standards. Exemple : Nous avons déjà 2 applications en chantier : SuiviAffaires (l'application principale) + SuiviAffaire_MàJ (mise à jour de la BDD). Admettons que nous voulions réutiliser, dans SuiviAffaires, le mécanisme qui permet à SuiviAffaire_MàJ - de vérifier si les tables sont bien attachées et, en cas d'erreur, - d'ouvrir la boîte de dialogue pour que l'utilisateur puisse indiquer l'emplacement de la base, puis, - reconnecter automatiquement toutes nos tables. nous allons avoir besoin de : - 2 classes : clApplication + clLog - au moins notre module 'APIsWindows', qui contient OuvrirFichierSpécifique()... - peut être d'autres Subs et Fonctions utilisées 2 fois ? Plutôt que de copier ces modules d'une application à l'autre, je conseillerais fortement de: - créer une base de données indépendante, par exemple Librairie.mdb - d'y copier (ou importer) les modules concernés. Puis, dans chacune des 2 applications (et au delà, dans toutes tes futures applications), - ajouter une simple référence à Librairie.mdb (dans VBA, menu Outils > Références) À moyen et long terme, les avantages sont considérables : un seul code à corriger et/où compléter, réutilisable partout. + le fait que la librairie peut être compilée en une Librairie.mde, donc code sécurisé ![]() L'inconvénient par contre : il n'est pas très pratique de déboguer un code dans une librairie partagée, même si c'est possible (pas dans un .mde). Notre cas est typique : nous avons trouvé une erreur dans une méthode (CheckConnections) appartenant à un des objets qu'on souhaiterait réutiliser ailleurs. Il faut donc impérativement 1- utiliser et déboguer complètement cette partie du code, tant qu'elle est incluse directement dans la 1ère application. C'est + facile, c'est ce que nous sommes en train de faire avec la classe clApplication, et c'est pour cette raison que nous avons du code de gestion d'erreur. Ça nous permet de nous arrêter et de corriger ou compléter cette méthode, ainsi que les routines comme Reconnect, qu'elle appelle. 2- une fois qu'elle aura été correctement déboguée, nous allons la déplacer dans la librairie partagée, pour pouvoir la réutiliser. Et c'est là, ou peut être déjà depuis un bon 1/4 d'heure ? que tu vas me dire : quel rapport avec notre contrôle d'erreur et les options d'arrêt ??? J'y arrive : tout cet exposé pour bien comprendre le principe suivant : Tout le code de notre application est contenu dans des routines qui s'appellent les unes les autres. Tout déclenchement de code part toujours - soit de la fonction de démarrage App_Init(), - soit d'un évènement de l'interface utilisateur : clic sur un bouton, lancement d'une commande dans un menu spécifique, saisie d'une valeur dans un contrôle... Les propriétés et les méthodes de nos objets (modules de classe), de même que tout ce que nous mettrons dans une librairie réutilisable, ne seront jamais appelés en direct, mais toujours à partir d'une des routines ci-dessus, déclenchée depuis l'interface. Par rapport aux tests ci dessus, elles ne seront jamais au niveau 0. Lorsque nous allons terminer notre application pour la distribuer à d'autres utilisateurs, nous allons 1- mettre du code de contrôle d'erreur à toutes les routines au niveau de l'interface utilisateur (dans tous les évènement de tous les formulaires et/ou de tous les états et de leurs contrôles) + dans la fonction de démarrage App_Init(). Grâce à ce contrôle d'erreur, nous sommes assurés que les utilisateurs de notre application : - ne verront jamais la boîte de dialogue avec options Débogage/Fin (ils ne sont pas censés rentrer dans le code pour déboguer) - que notre contrôle d'erreurs non prévues sera toujours actif, avec ses 3 options : Annuler/Réessayer/Ignorer - de +, si nous avons prévu d'enregistrer l'erreur dans un journal, toute erreur non prévue y sera bien enregistrée (ce qui nous sera beaucoup plus utile que les indications usuelles des utilisateurs qui ne retiennent jamais les messages d'erreur... )Même si l'erreur se produit dans une sub-sub-sub...routine, à n'importe quel niveau, elle sera contrôlée, affichée, enregistrée. 2- à l'inverse, nous n'allons mettre aucun code de contrôle d'erreur dans notre librairie. Ou disons, le strict minimum. Et pareil dans nos objets métier. Ceci est volontaire. Lorsqu'on appelle une commande de VBA, par exemple FileCopy et qu'une erreur se produit ("File not found", par exemple), l'erreur n'est pas gérée à l'intérieur de la commande FileCopy. Elle nous est renvoyée au niveau de notre application, pour que nous puisssions la traiter sur place et afficher les messages clairs à notre utilisateur ("Impossible de trouver le fichier Clients", par exemple). Nous devons, après les avoir correctement déboguées, considérer nos routines, encapsulées dans notre librairie, comme un complément de VBA, une surcouche du langage. Nos commandes de librairie (par exemple ThisApp.CheckTablesConnection) doivent être utilisées comme si elles faisaient partie d'une version étendue d'Access ou de VBA. Nous n'avons fait qu'ajouter une méthode personalisée à l'objet Application d'Access. Elles ne devraient pas afficher un message à l'utilisateur : c'est le programmeur de chaque application qui va - capturer l'erreur au niveau de l'appel, - personaliser chaque message dans la langue de l'utilisateur, et en fonction du contexte de chaque application. Elles vont donc se contenter de renvoyer les erreurs au niveau appelant : à l'application qui utilise la librairie. Et voilà la seule explication que je puisse trouver pour justifier la présence de l'option "Arrêt dans les modules de classe" qui sont, par définition, réutilisables et ne gèrent normalement pas les erreurs non prévues. Bien entendu, il y a des exceptions Regardons en une, que nous utilisons déjà, la fonction CopyFile(). Comme par hasard, l'original provient de ma librairie usuelle : je l'utilise partout, à la place de la commande VBA FileCopy() Lorsqu'elle aura été incluse dans une librairie, CopyFile() permettra, comme ici, - de contrôler que la source est bien différente de la destination. Note qu'elle n'affiche pas de message d'erreur : elle le renvoit avec un Err.Raise. Le programmeur de l'application principale pourra afficher ou non le message, ou gérer l'erreur dans son On Error Goto... - de gérer le fait que le fichier de destination peut déjà exister : faut-il le supprimer ou l'écraser, avec message ou sans rien dire... Là, je suppose que tu as remarqué un petit hic : la routine affiche éventuellement un message. Idéalement, il faudrait que le programmeur de chaque application puisse modifier ce message grâce à un des arguments d'appel de CopyFile(). Si d'autres erreurs non prévues se produisent, elles déclencheront le contrôle d'erreur de l'application principale qui pourra les gérer comme s'il s'agissait d'une erreur dans une commande VBA. Voili voilou. Si ces notions sont parfaitement claires : - distinction entre code avec ou sans gestion d'erreur, - niveaux d'appels de code entre les diverses routines, - comment VBA 'remonte' en cas d'erreur 'en bas', jusqu'à ce qu'il trouve une gestion d'erreur, - où faut-il gérer les erreurs non prévues et où est-il préférable de ne pas gérer les erreurs non prévues ? tu es à 2 doigts de créer des applications solides, que tu pourras déboguer rapidement, au fur et à mesure que les tests (préliminaires, beta tests ou déploiement réel) révèlent des erreurs non prévues. |
|||||||||||
|
|
00
|
|
|
#17 | ||
|
Membre du Club
![]() Inscription : mai 2006 Messages : 79 ![]() |
Citation:
Deux petites remarques (ou éclaircissement) Pas compris la différence entre un module de classe et un module ‘normal’ !!! Citation:
|
||
|
|
00
|
|
|
#18 | ||||||||
|
Membre Expert
![]() ![]() Etienne PailleretDéveloppeur VBA Inscription : mars 2004 Messages : 748 ![]() |
Citation:
Essayons un petit résumé : Module standard : - contient des variables Public ou Privées (avec Dim...) - contient des procédures Sub et Function, qui peuvent être Public, donc appelées depuis n'importe quel autre module, ou Private, donc utilisables seulement à partir d'une autre procédure du même module. Module de classe : (voir cours 05-Mise à jour BDD, les classes clApplication et clLog) - permet de créer un 'objet', doté de propriétés (=variables qui appartiennent à l'objet) et de méthodes (procédures qui appartiennent à l'objet). - les propriétés utilisent des Property Get et Property Let, (Voir Get/Let Name dans clLog) pour lire ou modifier la propriété. Raccourci : une variable publique dans le module de classe - voir Public Title as String, dans clApplication - n'a pas besoin des Property Letou Get. La valeur de la propriété est stockée dans une variable (mName ou Title, dans les exemples), à l'intérieur de l'objet. - les méthodes sont des Sub (qui ne renvoient rien) et des Function (qui peuvent renvoyer une valeur), comme dans un module standard. - les propriétés et les méthodes sont Public ou Private, comme dans un module standard. Note : Les librairies externes utilisent aussi des procédures Friend. Celles-ci sont 'publiques' dans toute la librairie elle-même, mais sont invisibles à l'application qui fait référence à cette librairie. On en reparlera peut être... L'utilisation d'un objet est différente de celle des variables et procédures standard : - tu dois d'abord créer une variable objet, du type de ta classe. 1- Déclaration de la variable : suivi de 2- Création de l'objet, qui est une instance (=une copie en mémoire) de la classe : L'utilisation, la modification d'une propriété ou d'une méthode se fait toujours en passant par le nom de la variable objet, suivi d'un point et de la propriété ou de la méthode. Code :
Code :
- Attention : un objet peut en cacher un autre, ce qui fait toute la puissance des objets. Voir notre clApplication qui contient un objet Log As clLog. Code :
ThisApp.Log.Name = "SuiviAffaires_MiseAJourBDD" Enfin, les objets peuvent être contenus dans des collections (voir exemple DAO ci-dessus, ou collection Forms() qui contient des objets Form...). Si nous avions besoin de plusieurs journaux dans notre moteur de mise à jour, nous aurions pu doter notre classe clApplication d'une collection de journaux, utilisables comme : Code :
Code :
ThisApp.Logs.Item("Journal d'erreurs - SuiviAffaires").Append Err.description ![]() Citation:
![]() Lors de modifications ultérieures de ton application, tu n'as plus besoin de distribuer la librairie (sauf si elle a été modifée aussi, oeuf corse). Voilà, si tout ça est bien clair (et y a intérêt 1- supprimant d'abord tout code d'erreur, sauf code de traitement d'une erreur spécifique, bien sûr. Ceci afin de repartir sur une base simple et claire. 2- d'extraire vers une librairie ce que nous pourrons réutiliser ailleurs. Par exemple, nos classes clApplication et clLog, pour que l'application principale soit elle aussi dotée d'un journal d'erreurs. 3- de choisir où nous allons mettre du code de contrôle d'erreur, et utiliser le générateur de mzTools pour 'blinder' nos 2 applications contre toute erreur imprévue. Donc, sauf questions ici que nous continuerons en //, prochain exercice : - reprendre la suite du cours 05, en supprimant tout code d'erreur non indispensable. |
||||||||
|
|
00
|
|
|
#19 | ||||
|
Membre du Club
![]() Inscription : mai 2006 Messages : 79 ![]() |
Citation:
Citation:
Citation:
Citation:
Nota : beaucoup de retard dans mes réponses,parce que ça ce complique(je m’accroche), que je ne comprend pas tout du premier coups et que j’ai un métier qui me sature en ce moment. (semaine 23 et semaine 24 pas là, en déplacement) |
||||
|
|
00
|
|
|
#20 | |||||
|
Membre Expert
![]() ![]() Etienne PailleretDéveloppeur VBA Inscription : mars 2004 Messages : 748 ![]() |
Bonjour, Serge,
je vois que l'enthousiasme et la bonne humeur sont toujours là Ça fait bien plaisir. Et puis, tant qu'il fait beau, on a le moral au top, non ? Citation:
Plus tard, (étape 3.), nous remettrons notre traitement d'erreurs non prévues (là où il faut, et pas plus). Le but est de bien distinguer les deux : dans la pratique, beaucoup mélangent allègrement le traitement des erreurs non prévues et celui des erreurs spécifiques, ce qui amène à des capharnaüms pas faciles à démêler ensuite... Donc, étape 1 : juste produire une appli qui, en cas d'erreur non prévue, ouvre la boîte de dialogue d'Access : Erreur... choix -> Déboguer ? ou Fin ? Citation:
Citation:
Tu as déjà démarré l'étape 2 et séparé la librairie du reste ?C'est un peu prématuré, et je comprends que tu satures. Encore une fois, Che va piano va sano, étape par étape. Citation:
2- avec les exercices que tu fais depuis quelques mois, j'ai le sentiment que tu vas, d'ici à l'année prochaine, nous pondre des applications qui seront beaucoup beaucoup plus "propres" (structure d'appels, contrôle d'erreur, commentaires...), mieux structurées (portée des variables, modules spécifiques/génériques, classes...), plus efficaces (optimisation, ergonomie, réutilisation de code...) que le plupart de celles, dites "professionnelles", sur lesquelles je tombe ici et là ![]() Citation:
Je préfère, au contraire, qu'on s'offre le luxe de prendre le temps (revoir la récré sur débogage et méthode de test, plus haut), pour bien éclaircir les quelques principes de base (y en a pas tellement). Et rien ne vaut la pratique : exercices, exercices, exercices... Donc, dès que tu as fait ton nettoyage (étape 1), avec quelques tests pour que ça compile et que ça marche quand même, tu le mets en pièce jointe et on revoit ça. |
|||||
|
|
00
|
Copyright © 2000-2012 - www.developpez.com