← Retourner à la page principale


Questions fréquentes et aides techniques en WLangage® *

* Les marques "PC SOFT®" et "WINDEV®" sont déposées par la société PC SOFT®.

 - L'AGL est récalcitrant, que faire ?
 - La gestion des erreurs et des exceptions
 - Précision des réels et des numériques
 - Comment se connecter à une base de données (HFSQL, MSSQL Server, Classic) ?
 - Comment consulter et afficher un document externe ?
 - Comment faire choisir un document externe ?
 - Comment optimiser l'affichage d'une table basée sur une requête ?
 - Analyse du mot-clé MesParamètres

Étapes à effectuer lorsque l'AGL WINDEV® est récalcitrant

Voici les étapes non-exhaustives lien externe à effectuer lorsque l'AGL WINDEV® est récalcitrant :
1) Réparer le projet (mise à niveau)
2) Recompiler le projet
3) Fermer le projet
4) Supprimer les dossiers "<NomDuProjet.cpl>", "Historique" et "Sauvegarde" du projet
5) (optionnel) Supprimer le fichier <NomDuProjet.env>
6) Ouvrir à nouveau le projet


Gestion des erreurs et des exceptions

En complément des pages d'aide PCSOFT® sur la Gestion des erreurs en exécution lien externe.
Il existe 2 types d'erreurs en exécution :
- les erreurs non fatales et non bloquantes (exemple : la lecture du contenu d'un fichier externe qui n'existe pas)
- les erreurs fatales et bloquantes (exemple : la division d'un nombre par zéro)

Gérer les erreurs non fatales

La gestion des erreurs non fatales peut être effectuée dans le traitement avec les solutions suivantes :

SI PAS <condition> ALORS
    // Code de traitement de l'erreur non fatale...
    // Pour obtenir toutes les informations sur la dernière erreur non fatale : 
    Trace(ErreurInfo(errComplet))
SINON
    // Suite du code...
FIN
SI ErreurDétectée() ALORS 
    // Code de traitement de l'erreur non fatale...
    // Pour obtenir toutes les informations sur la dernière erreur non fatale : 
    Trace(ErreurInfo(errComplet))
SINON
    // Suite du code...
FIN
ErreurChangeParamètre(...)
CAS ERREUR: 
    // Code de traitement de l'erreur non fatale...
    // Pour obtenir toutes les informations sur la dernière erreur non fatale : 
    Trace(ErreurInfo(errComplet))

- En cliquant sur le lien "Si Erreur : par programme" dans l'entête d'un code

Gérer les erreurs fatales / les exceptions

La gestion des erreurs fatales peut être effectuée dans le traitement avec les solutions suivantes :

QUAND EXCEPTION DANS
    // Code pouvant provoquer une erreur fatale...
FAIRE
    // Code de traitement de l'erreur fatale...
    // Pour obtenir toutes les informations sur la dernière erreur fatale : 
    Trace(ExceptionInfo(errComplet))
FIN
// Suite du code
CAS EXCEPTION:
    // Code de traitement de l'erreur fatale...
    // Pour obtenir toutes les informations sur la dernière erreur fatale : 
    Trace(ExceptionInfo(errComplet))

- En cliquant sur le lien "Quand exception : par programme" dans l'entête d'un code

Traiter une erreur fatale propagée au niveau du projet

Pour traiter une erreur fatale propagée au niveau du code d'initialisation du projet :

QUAND EXCEPTION
    sFichierException est une chaîne = ComplèteRep(fRepExe()) + DateVersChaîne(DateSys(),"AAMMJJ") +"_"+ "Exceptions.log"
    
    // Écriture du dump de débogage
    dbgSauveDumpDébogage(fExtraitChemin(sFichierException, fDisque+fFichier+fRépertoire) + ".wdump")
    
    // Écriture de l'erreur fatale dans un fichier log
    fSauveTexte(sFichierException, ...
        "------------------------------------------------------------------" +RC+ ...
        "------------------------------------------------------------------" +RC+ ...
        DateVersChaîne(DateSys(),"JJ/MM/AAAA")+"-"+HeureVersChaîne(HeureSys(),"HH:MM:SS")+" : " +RC+ ...
        "--------------------------------------------------------EXCEPTION-" +RC+ ...
        ExceptionInfo(errComplet) +RC+ ... // Dernière erreur fatale
        "-----------------------------------------------------------ERREUR-" +RC+ ...
        ErreurInfo(errComplet) +RC+ ... // Dernière erreur non fatale
        fChargeTexte(sFichierException) ...
    )
FIN

Précision des variables de type réel et numérique

En complément de la page d'aide PCSOFT® sur les variables de type réel lien externe.

rTemp1 est un réel
rTemp2 est un réel

// Utiliser la syntaxe 0n devant la valeur pour obtenir une précision maximale
rTemp1 = 0n1.234567
rTemp2 = 0n1.234568
// L'opérateur "=" est précis à 10e-6 près sur les réels
SI (rTemp1 <> rTemp2) ALORS
    // La conversion des réels en chaîne utilise un algorithme complexe donc la trace est correcte
    Trace( "NOK", rTemp1, rTemp2 )
FIN
rTemp1 = 0n1.2345678
rTemp2 = 0n1.2345679
SI (rTemp1 = rTemp2) ALORS
    // La conversion des réels en chaîne utilise un algorithme complexe donc la trace est correcte
    Trace( "OK", rTemp1, rTemp2 )
FIN


// (*) signifie que le format de la variable s'adapte automatiquement à la valeur contenue
xTemp1 est un numérique (*)
xTemp2 est un numérique (*)

// Utiliser 0n devant la valeur pour forcer l'affectation d'un type numérique
xTemp1 = 0n1.23456789012345678901234567890123456789
xTemp2 = 0n1.23456789012345778901234567890123456788
SI (xTemp1 <> xTemp2) ALORS
    Trace( "NOK", xTemp1, xTemp2 )
FIN
Pour les calculs avancés, le type réel est plus rapide que le type monétaire. De même que le type monétaire est plus rapide que le type numérique.

Variable numérique

Si une variable numérique est manipulée comme un opérateur logique (booléen) alors le zéro est équivalent à Faux et toutes les autres valeurs sont équivalentes à Vrai.
Donc les deux lignes de code sont équivalentes :
SI nIndice ALORS ... 
SI (nIndice <> 0) ALORS ... 
PCSOFT® recommande d'utiliser la première syntaxe.
Pour une optimisation du temps d'exécution du code alors utiliser la première syntaxe sinon pour une meilleure compréhension du code, utiliser la seconde syntaxe.


Procédures de connexion à une base de données

HFSQL client / serveur :

PROCÉDURE PUBLIQUE GLOBALE HConnexion_HFSQL(cnxConnexion est une Connexion, LOCALE sHPasse est une chaîne, LOCALE nNumPort est un entier sans signe, LOCALE sServeur est une chaîne, LOCALE sBaseDeDonnees est une chaîne, LOCALE sUtilisateur est une chaîne, LOCALE sMotDePasse est une chaîne) <métier> : booléen

// En cas d'erreur, on sort en renvoyant Faux et on propage l'erreur
ErreurChangeParamètre(epRenvoyerErreur, Faux)

// Permet de modifier le délai de connexion
HCS.DélaiRéponse = INTRANET // Temps d'attente de réponse faible 

// Permet de modifier la taille de la trame de données envoyée
// Dans le cas général, il n'est pas nécessaire d'intervenir sur la taille des trames
//HCS.TailleTrame = hTrameDéfaut

// Description de la connexion
cnxConnexion..Serveur 		= sServeur + ((nNumPort > 0) ? ":"+NumériqueVersChaîne(nNumPort) SINON "")
cnxConnexion..BaseDeDonnées 	= sBaseDeDonnees
cnxConnexion..Utilisateur 	= sUtilisateur
cnxConnexion..MotDePasse 	= sMotDePasse
cnxConnexion..Provider 		= hAccèsHFClientServeur
cnxConnexion..Accès 			= hOLectureEcriture
cnxConnexion..OptionsCurseur = hCurseurClient
cnxConnexion..InfosEtendues 	= ""
cnxConnexion..Cryptage 		= hCryptageRC5_16
cnxConnexion..Compression 	= (HCS.DélaiRéponse = INTERNET)

// Optimiser les temps de connexion aux bases HFSQL Client/Serveur : https://doc.pcsoft.fr/fr-FR/?9000176

// Affectation de la connexion à tous les fichiers de données
HChangeConnexion("*", cnxConnexion) 

// Fixe le mot de passe utilisé pour créer ou pour ouvrir les fichiers de données
SI PAS (sHPasse ~= "") ALORS
    HPasse("*", sHPasse)
FIN

// Ouverture de la connexion
HOuvreConnexion(cnxConnexion)

// Désactive la gestion des triggers applicatifs
HGèreTrigger(Faux)

// Gestion des caches dans le moteur HFSQL Client/Serveur (1 à 5000 - valeur par défaut sur un PC est 1000)
HGèreCache(hTaillePage, 2000)

SI PAS (sBaseDeDonnees ~= "") ALORS
    RENVOYER (Position(TAB+HListeBaseDeDonnées(cnxConnexion, hLstNormal)+TAB, TAB+sBaseDeDonnees+TAB) > 0)
SINON
    RENVOYER Vrai
FIN

MS SQL Server (avec le connecteur natif SQL Server lien externe - Accès Natif) :

PROCÉDURE PUBLIQUE GLOBALE HConnexion_MSSQL(cnxConnexion est une Connexion, LOCALE sServeur est une chaîne, LOCALE nNumPort est un entier sans signe, LOCALE sBaseDeDonnees est une chaîne, LOCALE sUtilisateur est une chaîne, LOCALE sMotDePasse est une chaîne) <métier> : booléen

// En cas d'erreur, on sort en renvoyant Faux et on propage l'erreur
ErreurChangeParamètre(epRenvoyerErreur, Faux)

// Description de la connexion
cnxConnexion..Serveur 		= sServeur + ((nNumPort > 0) ? ","+NumériqueVersChaîne(nNumPort) SINON "")
cnxConnexion..BaseDeDonnées 	= sBaseDeDonnees
cnxConnexion..Utilisateur 	= sUtilisateur
cnxConnexion..MotDePasse 	= sMotDePasse
cnxConnexion..Provider 		= hAccèsNatifSQLServer
cnxConnexion..Accès 			= hOLectureEcriture
cnxConnexion..OptionsCurseur = hCurseurClient
// WD Command Timeout : http://vincent-lecomte.blogspot.fr/2013/03/wd17-le-delai-dattente-de-la-requete.html
cnxConnexion..InfosEtendues = "Initial Catalog="+sServeur+"; WD Connection Timeout=10; WD Command Timeout=120" 

// Ouverture de la connexion
HOuvreConnexion(cnxConnexion)

// Affectation de la connexion à tous les fichiers de données
HChangeConnexion("*", cnxConnexion)

// Désactive la gestion des triggers applicatifs
HGèreTrigger(Faux)

RENVOYER Vrai

HFSQL Classic :

PROCÉDURE PUBLIQUE GLOBALE HConnexion_Classic(cnxConnexion est une Connexion, LOCALE sHPasse est une chaîne, LOCALE sCheminBDD est une chaîne)  : booléen

// En cas d'erreur, on sort en renvoyant Faux et on propage l'erreur
ErreurChangeParamètre(epRenvoyerErreur, Faux)

// Optimisation de l'accès aux données en HFSQL Classic.
HChangeLocalisation("*", hDisque)

// Description de la connexion
//VariableRAZ(cnxConnexion)
cnxConnexion..Serveur 		= ""
cnxConnexion..BaseDeDonnées 	= ""
cnxConnexion..Utilisateur 	= ""
cnxConnexion..MotDePasse 	= ""
cnxConnexion..Provider 		= hAccèsHF7
cnxConnexion..Accès 			= hOLectureEcriture
cnxConnexion..OptionsCurseur = hCurseurClient
cnxConnexion..InfosEtendues 	= ""
cnxConnexion..Cryptage 		= hCryptageRC5_16

// Affectation de la connexion à tous les fichiers de données
HChangeConnexion("*", cnxConnexion) 

// Fixe le mot de passe utilisé pour créer ou pour ouvrir les fichiers de données
SI PAS (sHPasse ~= "") ALORS
     HPasse("*", sHPasse)
FIN

// Ouverture de la connexion
HOuvreConnexion(cnxConnexion)

// Désactive la gestion des triggers applicatifs
HGèreTrigger(Faux)

// Modifie le chemin d'accès à la BDD
RENVOYER HChangeRep("*", ComplèteRep(sCheminBDD))


Consulter et afficher un document externe

	PROCÉDURE fLance(LOCALE sCheminEtNomDuFichier est une chaîne)
	// Ouvre le document dans une nouvelle fenêtre non modale (traitement de texte, tableur, lecteur PDF ou éditeur d'images)
	SI (OuvreDocument(sCheminEtNomDuFichier, "", odOptionFenêtreMaxi) = odEchec) ALORS 
	    // Ouvre directement le document dans son application associée
	    SI PAS LanceAppliAssociée(sCheminEtNomDuFichier, "open") ALORS
	        // Ouvre la fenêtre "Ouvrir avec..."
	        LanceAppli("rundll32.exe shell32.dll, OpenAs_RunDLL """+ sCheminEtNomDuFichier +"""")
	    FIN
	FIN
Un exemple d'utilisation :
fLance( "C:\logo.jpg" )
fLance(fRepExe() +[fSep()]+ "logo.jpg")


Faire sélectionner un document externe à l'utilisateur

	PROCÉDURE fSélectionne(LOCALE sExtensionParDefaut est une chaîne = "jpg") : booléen
	sCheminEtNomDuFichier est une chaîne
	
	SI PAS (sExtensionParDefaut ~= "jpg") ALORS
	     // Ouvre le sélecteur de fichiers avec les extensions de fichier
	     sCheminEtNomDuFichier = fSélecteur("", "", "Sélectionnez un fichier...", "Tous les fichiers (*.*)" +TAB+ "*.*" +RC+ "Traitement de texte" +TAB+ "*.doc;*.docx;*.odt;*.rtf" +RC+ "Tableur" +TAB+ "*.xls;*.xlsx;*.ots;*.csv" +RC+ "Document PDF" +TAB+ "*.pdf" +RC+ "Image" +TAB+ "*.jpg;*.jpeg;*.gif;*.png;*.bmp;*.svg", sExtensionParDefaut)
	SINON
	     // Ouvre le sélecteur d'image uniquement avec les extensions de fichier "Image"
	     sCheminEtNomDuFichier = fSélecteurImage("", "", "Sélectionnez une image...")
	     SI PAS fEstUneImage(sCheminEtNomDuFichier) ALORS
	          RENVOYER faux
	     FIN
	FIN
	
	// Teste l'existence du fichier externe
	RENVOYER fFichierExiste(sCheminEtNomDuFichier) 
Un exemple d'utilisation :
fSélectionne("jpg")
fSélectionne("docx")
fSélectionne("")


Optimiser l'affichage d'un champ table basé sur une requête

1) Tester la requête dans le SGBD pour mesurer son temps d'exécution :
   - copier / coller le code SQL de la requête dans le SGBD
   - remplacer à la main les paramètres nécessaire pour la requête
   - lancer l'exécution de la requête
   - si le temps d'exécution est trop important alors modifier le script SQL
2) Tester la requête dans l'éditeur de requête pour mesurer son temps d'exécution :
   - créer une nouvelle requête puis copier / coller le code SQL dans l'éditeur de requête
   - faire un Go uniquement de la requête (tester l'élément) puis saisir les paramètres demandés et nécessaire
   - si le temps d'exécution est trop important alors vérifier la connexion avec la base de données (matériel, réseau, port, anti-virus, etc...) et également l'utilisation de fonctions comme HSimuleRéseau()
3) Dans la fenêtre du champ table :
   - utiliser les propriétés ..AffichageActif et ..TotauxActif :

	<NomChampTable>..AffichageActif = Faux
	<NomChampTable>..TotauxActif = Faux
	// Exécution de la requête
	// Traitements  : TableAffiche(), etc...
	<NomChampTable>..TotauxActif = Vrai
	<NomChampTable>..AffichageActif = Vrai
   - vérifier le mode d'initialisation de la requête : hRequeteSansCorrection / hRequeteSansCorrectionHF / etc...
   - vérifier le temps d'exécution cumulé de tous les événements du champ table, principalement : "Affichage d'une ligne" et "Fin d'initialisation"
   - utiliser l'analyseur de performance dans le traitement lent (AnalyseurDébut et AnalyseurFin) afin de cibler précisement le traitement qui provoque la lenteur


Analyse du mot-clé MesParamètres

Le mot-clé MesParamètres lien externe permet de manipuler tous les paramètres d'une procédure.

Par exemple, la procédure ci-dessous calcule la somme de ses 3 paramètres en entrée :

PROCÉDURE CalculerSomme(nX est un entier, nY est un entier, nZ est un entier) : entier
    nSomme est un entier
    POUR i = 1 _À_ MesParamètres..Occurrence
        nSomme += MesParamètres[i] // nSomme = nSomme + MesParamètres[i] 
    FIN
    RENVOYER nSomme
FIN

// Le résultat de la trace vaut 7 
Trace( CalculerSomme(1, 2, 4) )

L'exemple ci-dessus est une procédure avec seulement 3 paramètres.
Et donc la procédure peut être écrite simplement sans utiliser le mot-clé MesParamètres :
PROCÉDURE CalculerSomme(nX est un entier, nY est un entier, nZ est un entier) : entier
    RENVOYER (nX + nY + nZ)
FIN

Mais pourrait-on faire une procédure générique qui calcule la somme de tous ses paramètres sans en connaitre le nombre exact ? Oui, grâce à l'utilisation du signe "*".
// Le nombre de paramètres passé à la procédure est variable
PROCÉDURE CalculerSomme(*) : entier
    nSomme est un entier
    POUR i = 1 _À_ MesParamètres..Occurrence
        nSomme += MesParamètres[i] 
    FIN
    RENVOYER nSomme
FIN

// Le résultat de la trace vaut 15 
Trace( CalculerSomme(1, 2, 4, 8) )

L'exemple ci-dessus est une procédure avec des paramètres ayant le même type (entier).
Et donc la procédure peut être écrite simplement sans utiliser le mot-clé MesParamètres :
PROCÉDURE CalculerSomme(tabEntier est un tableau d'entier) : entier
    RENVOYER Somme(tabEntier)
FIN

// Le résultat de la trace vaut 15 
Trace( CalculerSomme([1, 2, 4, 8]) )

Le mot-clé MesParamètres est puissant car il accepte aussi les paramètres de type différent. Et ils peuvent directement être passés à une autre procédure / fonction.
PROCÉDURE OuvrirFenetre(sNomFenêtre est une chaîne <nom de fenêtre>, *)
    Trace(sNomFenêtre)
    Ouvre(sNomFenêtre, MesParamètres[2 A])
FIN