Vous êtes sur la page 1sur 125

‰ A propos du Guide...

‰ Les auteurs, qui sommes-nous ?


‰ Conventions d'écriture de l'ouvrage
‰ Déclarations de variables - Ecriture du code
‰ Déclarations de structures
‰ Fichiers externes .WL
‰ Fichiers externes .INI
‰ Fichiers REP
‰ Librairies WinDev WDL
‰ Utilisation du débogueur interne
‰ Programmation en réseau
‰ Résolution des problèmes sur les bases HyperFile
‰ Importation et Exportation de fichiers
‰ Dates et Heures
‰ Les Timers
‰ Création et utilisation de Classes
‰ Programmation des ActiveX
‰ Utilisation des API Windows
‰ Liens OLE avec Word
‰ Liens OLE avec Excel
‰ Les Tree-View : Drag & Drop et Impression
‰ Fichiers xBase
‰ Gestion des documents RTF
‰ WinDev - Développement Internet
‰ WinDev et le Minitel
‰ Faxer avec WinDev
‰ Scanner avec WinDev
‰ Utilisation des Codes-Barres
‰ Effets spéciaux sur images
‰ Plus de 70 Routines ou Fonctions
‰ Les secrets de SendMessage()
‰ Protection des applications développées
‰ Développer une application fonctionnant sur Cd-rom
‰ InfoObjet(), une fonction non documentée
‰ Valeurs des constantes Windows
‰ Liste des messages d'erreurs Windows
‰ Trucs et Astuces Windows
‰ Outils externes complémentaires du développeur
‰ Sites ou forums pour développeurs
Le Guide !
Ce guide a pour objectif de faire gagner un temps précieux aux programmeurs, débutants ou
chevronnés, qui développent des applications avec l'outil L4G WinDev.

Hormis la documentation « officielle » de PcSoft, il n'existe actuellement aucun ouvrage en librairie,


virtuelle ou non, traitant de WinDev et c'est fort dommage.

Nous avons souhaité combler cette lacune avec cette première édition et nous sommes certains que
vous apprécierez ce livre et y trouverez matière à réutilisation, sinon à réflexion.

Il a été élaboré en utilisant la dernière version de WinDev, la version 5.5b qui était disponible lors de
la rédaction de cet ouvrage. Aussi, tous les efforts ont été mis en œuvre pour vous offrir des
procédures, fonctions et classes fonctionnant parfaitement avec cette version.

Les sources fournies pour chacun des sujets traités dans ce livre sont largement commentées et
réutilisables immédiatement dans vos applications WinDev.

Vous n'avez pas de royalties ou de licence à payer, ni à citer les sources utilisées dans vos
applications.

Chaque développeur ou société ayant acquis la version électronique ou papier du Guide est
titulaire d'un N° personnel unique correspondant à l'installation sur UNE machine.

Toute personne de la société en question peut donc lire et utiliser les informations sur cette
machine. Il est toutefois interdit de vendre ou donner les sources et projets fournis à toute
personne n'ayant pas acheté le Guide. En clair, VOUS avez acheté le guide, vous pouvez
utiliser les sources et exemples dans VOS projets mais vous ne pouvez pas les donner /
vendre à une autre personne / société.

C'est pourquoi, nous vous fournissons un code de débridage qui vous est personnel.

Nous avons voulu vous offrir des sources prêtes à l'emploi, dans les domaines les plus variés,
domaines qui sont le pain quotidien dans nos activités de développement.

Tous les exemples ont été plusieurs fois testés en environnement Windows 98 2ème édition, avant
d'être intégrés dans cet ouvrages. Ils ont été compilés en 32 bits sur des machines à processeurs
Intel III, 700 Mhz, avec 128 Mo de Ram et disques durs IDE 15 Go.

En principe, ces exemples fonctionnent également parfaitement sous Windows 95, Windows NT4.
Cependant, selon la configuration de votre machine, quelques ajustements peuvent s'avérer
nécessaires.

Développeurs WinDev depuis l'aube, nous avons apporté le plus grand soin à la rédaction de ce livre
et nous vous remercions de bien vouloir nous faire part de vos rermarques, critiques, suggestions et,
pourquoi pas, de vos compliments.

Nous espérons avoir atteint l'objectif de vous faire gagner du temps et de mieux faire connaître
WinDev, à notre avis largement méconnu !
Bons développements à tous avec WinDev !

Les auteurs…

Alain Duglas Fabrice Harari

WINDEV® est un logiciel de développement RAD produit par PC-SOFT®

Les mots ''Windows, ''NT'', "WINDEV®", "PC-SOFT®" "HyperFile" et "LST®" sont la


Propriété de leurs propriétaires respectifs et ne sont utilisés ici qu'en tant que références.

Il n'y a aucun lien direct ou d'affiliation entre la société PC-SOFT® et les auteurs du présent ouvrage.
Fabrice HARARI
___________________________________________________________________

Titulaire d'un simple DUT informatique, Fabrice Harari a commencé sa carrière d'informaticien en
1985 directement comme chef du service informatique d'une SSII Niçoise. Sa volonté
d'indépendance l'a amené en 1987 à créer sa propre société, Axlinfo. Cette société travaillait à
l'origine dans le monde Prologue, en Bal, Abal pour développer des logiciels de gestion, mais aussi
en assembleur pour développer des outils systèmes. Le virage vers Windows sera pris en 1994 avec
WinDev 1.5, le C/C++ et l'assembleur.

Depuis cette date de nombreux logiciels de gestion mais aussi des utilitaires (comme AxlSauve,
logiciel gratuit de sauvegarde sur disque amovible) auront été développé avec WinDev.

Il a créé en décembre 1999 et administre depuis cette date, le site Internet devenu depuis
http://www.WinDevasso.org. Impliqué depuis plusieurs années dans le WinDev-Forum, LA mailing
liste technique sur WinDev, il a activement participé à la création de l'Association des Développeurs
WinDev (WinDevAsso) en octobre 2000.

Aujourd'hui président de cette association, il effectue, en plus de son travail au sein d'Axlinfo, des
missions de Conseil / Audit et de gestion de projet de développement WinDev en France et à
l'étranger.

La rédaction de ce livre sur WinDev s'inscrit donc totalement dans l'évolution de sa carrière, en
mettant au service de tous les développeurs WinDev les fruits de son expérience.

Contact : fharari@axlinfo.com

Alain DUGLAS
___________________________________________________________________

Commercial de formation et d'expérience, Alain Duglas a travaillé jusqu'en 1985 au sein de


multinationales étrangères comme Rothmans International ou Colgate Palmolive en tant que directeur
des ventes et de la promotion, directeur de filiale, directeur commercial…

Après l'achat de son premier micro-ordinateur au début des années 80 (un Apple IIe..), il est très vite
passionné par l'informatique. Pur autodidacte, créateur et animateur de deux clubs micro (Institut
Français de Gestion, Automobile Club du Rhône), il crée sa première SSII en 1985, Microfil, société
qu'il animera jusqu'en 1995.

Cette SSII de 8 personnes est spécialisée dans le développement de programmes de gestion


spécifiques destinées aux PME et aux industriels. Le langage phare utilisé alors est Clipper (Summer
87) puis Nantucket Clipper 5, toujours sur des bases de données xBase. Puis arrive Windows et on
passe alors au langage DbFast de l'éditeur Computer Associates, puis à WinDev, qui promet un gain
de temps de développement énorme…
En 1995, souhaitant se consacrer à des développements plus « personnels » et plus attractifs, il crée
DIACOM International, une société lyonnaise d'édition, spécialisée dans les développements créatifs
destinés au grand public, logiciels diffusés via d'autres éditeurs et la grande distribution.

Il est également auteur depuis près de 5 ans auprès d'éditeurs prestigieux comme GT Interactive
Software, Sybex... et développe exclusivement avec WinDev depuis 1994, mais ne refuse pas, à
l'occasion, à mettre les mains dans le cambouis d'autres langages, comme Visual Basic ou Delphi...

Il est l'auteur en 1999 de la classe RichText WinDev, diffusée avec succès à plusieurs centaines
d'exemplaires auprès de la communauté WinDev en France et à l'étranger.

Il est également l'auteur du seul générateur aléatoire de grilles de Mots Croisés français au monde,
développé en WinDev « pur sucre », Super Mots Croisés, vendu à plusieurs milliers d'exemplaires
depuis Avril 1999, via GT Interactive et le réseau commercial de TF1 Vidéo. Ce logiciel a nécessité
deux ans de réflexion et une année complète de développement.

Pratiquant assidu et expérimenté de WinDev, il souhaite, avec cet ouvrage écrit à quatre mains, faire
partager son expérience et son plaisir de développer avec WinDev.

Contact : DIACOM@aol.com
Conventions d'écriture de l'ouvrage

Afin de vous repérer rapidement dans cet ouvrage et dans leurs chapitres, la notation suivante a été
adoptée, les polices correspondantes avec les tailles et les couleurs ci-dessous :

1. Chapitres,

2. explications et commentaires concernant le sujet traité,

3. points importants à retenir,

4. des commentaires supplémentaires,

5. procédures ou étapes à suivre,

6. sources WinDev : chaque début de source est signalé par l'icône

7. exemples du cédérom compagnon : ils sont repérés par l'icône ,

8. notes particulières : elles sont repérées par l'icône .

9. liens à des sites Internet ou adresses e-mail : www.site_internet.com .

OoOoOoOoOoOoOoOoO
Déclarations de Variables - Ecriture du Code

« Tous les chemins mènent à Rome... » indique l'expression populaire.

C'est particulièrement vrai dans notre métier !

C'est certain, chaque développeur a ses petites habitudes, sa méthodologie (ou parfois... l'absence
de méthodologie !), sa façon d'écrire son code ainsi que son style propre.

Evidemment, à priori, ce style n'a pas d'incidence sur le bon fonctionnement de l'application finale.

Pourtant, parfois, lorsque l'on regarde le code de certaines applications, on prend peur !

Imaginez la reprise d'un source vieux de quelques années, source dont il n'existe pas de dossier, et
qui n'a pas été écrit par vous !

C'est pourquoi, nous avons jugé utile de rappeler quelques conseils de base, notamment concernant
la notation hongroise.

Ce terme savant recouvre simplement une façon d'écrire son code, un style universel (ou qui devrait
l'être !) reconnaissable immédiatement par des programmeurs avertis.

La notation hongroise est un ensemble de conventions popularisé il y a de nombreuses années par


Charles Simonyi lorsqu'il travaillait chez Microsoft.

WinDev n'étant pas un langage fortement typé, cette convention de codage comporte énormément
d'avantages :

• lisibilité et compréhension immédiate d'un source,


• différenciation des variables globales et locales,
• identification d'un seul coup d'œil du type d'une variable,
• gain de temps appréciable lors de la modification de sources.

Si vous n'utilisez pas (pas encore...) la notation hongroise, vous pouvez, en quelques jours, acquérir
des réflexes d'écriture qui vous aideront grandement...

En voici les règles principales, simples à utiliser : si vous avez, par exemple, une variable numérique
pour un total mensuel, nommez-la ngTotalMensuel.

Qu'y a-t-il de spécial dans cette notation ?

Tout d'abord, pour une meilleure lisibilité le début de chaque « mot » constituant la variable
commence toujours par une Majuscule. Ensuite, les deux premiers caractères de cette variable,
toujours en minuscules, indiquent sa nature : la première, son type : n pour numérique, la seconde,
sa portée : g pour globale.
Nous avons donc ici une variable ngTotalMensuel globale de type numérique correspondant à la
somme d'un total mensuel.

Autre exemple : clNomUtilisateur indique qu'il s'agit d'une variable locale, de type chaîne
correspondant vraisemblablement au nom d'un utilisateur. Par ailleurs, le choix du nom de cette
variable est très utile car d'une simple lecture, vous savez à quoi sert celle-ci, même si vous n'êtes
pas l'auteur du source.

« Le style, c'est l'homme » disait Buffon. C'est pourquoi, sans vouloir choquer quiconque, certains
développeurs feraient bien de s'inspirer de cette maxime !

On peut ainsi « typer » chaque variable selon sa nature :

c Chaîne,
d Date,
l Booléen,
m Monétaire,
n Numérique,
t Tableau
etc.

Bien évidemment, la notation hongroise n'apporte aucun avantage en vitesse de traitement, mais elle
est fortement recommandée pour tous les avantages décrits précédemment.

N'hésitez pas à utiliser des noms longs sous WinDev, vous avez droit à 33 caractères (information
donnée par le ST de PcSoft).

: lors de la description de vos fichiers, le nom de vos rubriques (champs...) ne peuvent dépasser
23 caractères...

Ainsi, les deux variables cgUtilisateurDirection et cgUtilisateurServiceTechnique seront


parfaitement différenciées lors de l'exécution de votre code.

Certains programmeurs utilisent le caractère souligné « _ » pour encore mieux séparer et « éclaircir »
leur code, comme par exemple :

cg_Utilisateur_Direction ou cg_Utilisateur_Service_Technique

Cette notation a le mérite d'être encore plus lisible, simplement faites attention à la longueur
maximale de la variable, ne dépassez jamais 33 caractères !

Les noms de fichiers et les Alias

Les noms de fichiers et les alias sont toujours en MAJUSCULES, comme CLIENTS, FACTURES, CL.
, FC. GL. etc.

Les noms de champs


Les noms de champs sont eux aussi en majuscules : CL.NOMCLIENT, FC.NUMFACT

Les procédures et les fonctions

Dans l'écriture de vos fonctions ou procédure acceptant des paramètres en entrée, il est judicieux de
préfixer les noms des paramètres par « p » :

Fonction Arrondi (pnValeur1, pnValeur2)

Ici, on sait que l'on attend en entrée de fonction 2 valeurs numériques et qu'il s'agit de paramètres...

Indiquez, également, immédiatement au-dessous de la déclaration de chaque fonction ou procédure


A QUOI SERT celle-ci !

En effet, trop souvent, on lit des sources avec des noms de procédures ou de fonctions abscons,
obscurs, incompréhensibles en première lecture.

Donnez des noms « parlants » à vos procédures et fonctions, n'omettez pas d'expliquer en une ligne
ou deux ce que fait cette fonction ou procédure.

Vous vous en porterez mieux, gagnerez énormément de temps et aurez la satisfaction personnelle de
pouvoir vous relire instantanément.

Nous ne parlons même pas ici du travail de développement en groupware !

Imaginez si pour des tas de raisons, vous passez du statut de développeur indépendant isolé à celui
de développeur en équipe...

Vous risquez d'avoir beaucoup de mal à vous faire accepter au sein du groupe !

Les déclarations

Les variables sont regroupées par type et par fonctionnalités. Elles peuvent être déclarées sur une ou
plusieurs lignes :

Local
// Dates de l'agenda
clMoisEnCours, clMoisSuivant sont des chaines = ""

Les commentaires

Que celui qui n'a jamais hésité à la relecture d'un de ses sources après quelque temps se lève !

Notre métier étant assez particulier, il nous arrive fréquemment d'avoir une idée et de la mener
jusqu'à son terme, afin de vérifier la validité de cette idée. Et puis, si cette idée est viable, on
poursuit...

Seulement, les pensées sont fugitives et si vous ne commentez pas cette brillante idée
immédiatement après l'avoir écrite et testée, il y a de fortes chances pour que dans 6 mois, lors de
modifications, vous vous grattiez la tête avec quelques hésitations !

Alors, commentez vos sources, commentez, commentez !!!

Le gain de temps apporté par des commentaires est inestimable et vous vous féliciterez tous les jours
de l'avoir fait.

La bonne méthode pour commenter ses sources est de faire des phrases en français, comme par
exemple :

// Boucle d'itération sur le nombre de lignes


// en lecture du fichier externe FRAMES.TXT

Il est également important (pour le futur...) de commenter ses variables par une courte description,
comme par exemple :

Global
nCarteBancaire, nNumeroCarte sont des entiers longs = 0 // Gestion des Cartes de Crédit
cDateValidité est une chaine = "" // Date limite d'utilisation des cartes de crédit

Vous n'aurez ainsi aucun mal à vous relire et si une autre personne d'autre doit intervenir sur vos
sources, vous lui faciliterez grandement la tâche...

Pensez également à faire en sorte que chaque ligne soit directement lisible dans la fenêtre de
l'éditeur, sans qu'il soit nécessaire de faire appel à l'ascenseur horizontal (autre gain de temps ...) que
ce soit dans l'écriture de vos commentaires comme dans le source proprement dit :

N'écrivez pas :

// Début de la procédure de scrutation du port série, nécessitée par la lecture...

Ici, le lecteur est obligé de faire appel à l'ascenseur pour lire la totalité du commentaire...

Ecrivez plutôt :

// Début de la procédure de scrutation du port série,


// nécessitée par la lecture en entrée octet par
// octet du poids issu de la balance marchandises.

Dans vos sources, utilisez la continuation des lignes, en interrompant le code par « ... »
et continuez sur la ligne suivante

Les Classes

Les classes, elles aussi, gagnent à être commentées...

Que préférez-vous ?
Ce code :

PlanningJournalier est une Classe


// Dépendances
//=======================================
// Nom du champ image destination (obligatoire)
NomChampImage est une chaine
// Nom de la fenêtre qui contient le champ
NomFenetre est une chaine
//Nom du champ Bulle
NomChampBulle est une chaine
// Numéro de poste
NumPoste est un entier

// Dates
//=======================================
// Date affichée en 1ère colonne

ou celui-ci :

Planj est une classe


NCIM, NCF,NCB sont des chaines
NCPest un entier

Le résultat sera le même, mais......... !

Le caractère de soulignement

Comme dit, précédemment, vous pouvez utiliser le caractère de soulignement « _ » à l'intérieur d'une
variable.

Cependant, il est préférable de ne pas commencer un nom de variable par ce caractère. En effet,
PcSoft donnant au compte-gouttes le nom de certaines de ses variables internes, vous risquez de
rentrer en collision avec celles-ci.

L'indentation

L'indentation fait partie également des bons principes à adopter.

Elle assure une meilleure lisibilité de votre code, permet de vérifier les débuts et fins de boucles et,
dans des boucles imbriquées compliquées, elle est fort utile.

A ce propos, n'hésitez pas à ajouter un commentaire à vos fins de boucle comme par exemple :

FIN // Fin de Si
FIN // Fin de Selon
FIN // Fin de TantQue

Visual Basic intègre en natif cette particularité d'écriture, permettant d'écrire, par exemple :

End If
End Select

bien pratique pour se repérer dans un source long et compliqué, mais WinDev n'ayant pas celle-ci,
vous pouvez y suppléer avec des commentaires.

: vous pouvez régler la largeur de chaque tabulation dans le menu WinDev par « Outils », «
Options », sélectionnez l'onglet Code puis saisissez la valeur souhaitée d'une tabulation dans le
champ « Largeur des tabulations » en nombre de caractères...

Conclusion

En appliquant ces quelques règles simples, vous améliorerez votre style, gagnerez du temps, et
aurez du plaisir à vous relire (et en procurerez à ceux qui vous liront !).

Comme quoi, les bons outils font (parfois...) les bons ouvriers. La notation hongroise fait partie de ces
bons outils !

oOoOoOoOoOoOoOo
Déclaration de structures

Les structures sont très utilisées en programmation. Elles sont parfois indispensables dans certains
traitements, et notamment dans le passage de paramètres lors de certains appels faits à des
fonctions API Windows.

Une structure correspond à un type abstrait de données défini par le développeur et non implémenté
dans un langage de programmation.

Vous pouvez déclarer une ou plusieurs structures en gardant à l'esprit :

• Qu'une structure, dès qu'elle est déclarée, sera publique. Ce qui signifie qu'elle sera
accessible n'importe où dans votre application,
• Qu'il n'est pas possible de déclarer une structure locale ou privée dans WinDev, à la
différence d'autres langages,
• Si une structure est déclarée dans le code d'initialisation d'une fenêtre, elle restera publique et
reconnue dans tous les autres traitements, même si la fenêtre dans laquelle la structure a été
déclarée a été fermée.

Ces notions ont énormément d'importance et notamment en terme de mémoire vive, car toutes les
structures déclarées dans une application, restent en mémoire, où qu'elles aient été déclarées.

Les variables typées peuvent être passées en paramètres à des fonctions et le typage de la variable
dans la fonction est automatique.

La syntaxe pour déclarer une structure est la suivante :

« NomStructure » est une structure


« NomMembreStructure1 » est un « TypedeVariable1 »
« NomMembreStructure2 » est un « TypedeVariable2 »
« NomMembreStructure3 » est un « TypedeVariable3 »
.......
Fin

« NomStructure » est le nom identifiant la structure,


« NomMembreStructure1 » est le nom du 1er membre de la structure,
« TypedeVariable1 » est le type du 1er membre de la structure.
Note importante :

Une structure peut faire référence à une autre structure (ou à plusieurs autres structures..)
déjà définie(s).

La syntaxe permettant de déclarer une structure faisant appel à une autre structure est :

« NomStructure » est une structure


Un objet « NomAutreStructure »
Un objet « NomEncoreUneAutreStructure »
« NomMembreStructure1 » est un « TypedeVariable1 »
Fin

« NomAutreStructure » et « NomEncoreUneAutreStructure » sont les noms des autres structures déjà


définies...

La syntaxe pour déclarer une variable typée (structurée) est la suivante :

« NomVariable » est une « NomStructure »

« NomVariable » est le nom de la variable typée.

Une variable typée se manipule par les membres de sa structure.

La syntaxe pour utiliser une variable typée est :

« NomVariable » : « NomMembre »
Exemple de déclaration de structure:

// Déclaration de la structure
Coord est une Structure
PosX est un entier
PosY est un entier
Fin

// Utilisation de la structure
PosSouris est une Coord
PosSouris :PosX=25
PosSouris :PosY=15

Autre exemple de déclaration de structure :

_SystemTime est une structure


Wyear est entier
WMonth est un entier
WDOW est un entier
Wday est un entier
Whour est un entier
Wminute est un entier
Wsecond est un entier
Wmsec est un entier
FIN

Autre exemple de déclaration de structure :

_W32FileDATA est une structure


DWFA est un entier long
ZHCdwLowDateTime est un entier Long
ZHCdwHighDateTime est un entier long
ZLAdwLowDateTime est un entier Long
ZLAdwHighDateTime est un entier long
ZLWdwLowDateTime est un entier Long
ZLWdwHighDateTime est un entier long
NfSh est un entier long
NfSl est un entier long
RES0 est un entier long
RES1 est un entier long
NOMCOMP est une chaine fixe sur 260
Nomcour est une chaine fixe sur 14
Fin

Autre exemple de déclaration de structure :


Fichiers externes WL

Vous le saviez sans doute déjà, mais n'importe quel fichier externe contenant du texte ASCII peut
être inclus dans une application WinDev par la déclaration EXTERNE Fichier.Extension :

EXTERNE WINCONST.WL

Le résultat est identique à une copie du fichier texte sous l'éditeur de code, à la place de la ligne
"EXTERNE".

Vous pouvez donc inclure des instructions WinDev au point d'insertion représenté par
EXTERNE...

Il est possible de spécifier un chemin complet :

Exemple : "D:\PROJET\DATA\MESCONST.WL").

Si le chemin n'est pas spécifié, le fichier est recherché successivement :

· dans le répertoire du projet


· dans le répertoire de WinDev

Remarques :

- Le fichier doit être un fichier texte classique, créé sous n'importe quel éditeur de texte.
- L'inclusion de fichier est particulièrement utile pour décrire des constantes communes à
plusieurs projets ou au système d'exploitation.

Dans ce cas, l'endroit idéal pour placer la ligne EXTERNE "fichier" est le code d'initialisation du projet,
ce qui permettra aux constantes d'être accessibles partout dans le projet.

Rien ne vous empêche, par exemple, d'insérer la ligne suivante dans un fichier nommé
« CODE.WL » :

Info("Il est "+ heureverschaine(heuresys())+ "! "+RC+"Il serait temps de vous mettre au travail ! ")

Cette ligne sera alors interprétée comme du code WinDev « en dur » dans votre application.

A l'ouverture de l'application, la boite de dialogue correspondante s'ouvrira avec la valeur


d'HeureSys()...

Mais vous pouvez également écrire ceci dans un fichier externe :

GLOBAL
rect est compose de
l est un entier long
t est un entier long
r est un entier long
b est un entier long
fin

puis dans votre application, n'importe où, écrire, par exemple :

RECT.R = 80
RECT.L=30
HORZRES est un entier long = rect.r - rect.l
INFO(HORZRES)

La seule restriction concerne bien évidemment du code externe faisant appel à des variables non
encore déclarées ou non initialisées, et, bien sûr la présence du fichier en question...

Par ailleurs, si une modification du fichier externe à lier est effectuée, vous devez à chaque
modification, réinitialiser le lien vers ce fichier en ouvrant l'éditeur de code WinDev.

Ces quelques lignes vous montrent tout l'intérêt de l'usage de fichiers externes dans lequel il n'y a
que la limite de votre imagination (ou de vos besoins... ) !

Insérer des constantes dans une application

Deux fichiers définissant des constantes sont fournis avec WinDev : "WinConst.wl" (constantes
standard de Windows), et "Except.wl" (constantes des exceptions W-Langage).

Mais rien ne vous empêche de déclarer des constantes personnelles, relatives à votre application. Il
vous suffit de respecter les règles définies ci-avant...

Vous pouvez donc avoir des constantes globales identiques à tous vos projets, si vous les placez
dans un fichier externe et que vous insérez la ligne suivante dans le code d'initialisation de ces
projets :

EXTERNE "C:\PROJETS\CONSTANT.TXT"

Déclarer un objet externe

Le mot clé EXTERNE sert à déclarer une variable que n'est pas réellement déclarée.

Par exemple dans un projet qui charge une analyse ou une bibliothèque et utilise ses éléments.

Lors de la compilation, ces éléments sont inconnus.

Externe permet de les déclarer pour éviter un Warning lors de la compilation.

Voilà, vous connaissez l'essentiel à présent sur les fichiers WL et comment les utiliser avec profit !

oOoOoOoOoOoOo
Fichiers externes .INI

Les fichiers INI, qui sont des fichiers textes externes à n'importe quelle application Windows, sont
encore souvent peu utilisés par les développeurs.

C'est fort dommage car leur utilisation permet de paramétrer, de façon externe, toutes sortes de
fonctionnalités dans vos applications.

Vous pouvez utiliser ou plusieurs fichiers INI dans une application. Vous pouvez également utiliser le
fichier WIN.INI (par défaut…).

Ces fichiers peuvent être placés dans le répertoire d'installation de votre application, dans le
répertoire Windows, ou dans tout autre endroit qu'il vous plaira, sous réserve d'indiquer le chemin
complet du fichier INI que vous voulez traiter.

Vous pouvez y placer une foultitude de choses : paramètres d'initialisation de vos applications, mots
de passe cryptés, préférences utilisateur et bien d'autres choses encore !

Les fichiers INI peuvent être utilisés dans les versions de Windows 3.1, 9x, NT…

N'oubliez pas cependant que la taille maximale d'un fichier INI est limité à 64 Ko…

Les fichiers INI sont structurés de cette façon :

[SECTION_1]
Mot Clé_1=……………
Mot Clé_2=……………

[SECTION_2]
Mot Clé_1=……………
Mot Clé_2=……………

etc…

WinDev possède deux fonctions simples et puissantes permettant de créer des fichiers INI, d'en lire
le contenu ou de modifier celui-ci.

INIECRIT() permet d'écrire dans un fichier INI. Si le fichier INI n'existe pas et si le chemin donné à ce
fichier est correct, il est créé automatiquement.

INILIT() permet de lire un Mot Clé d'une SECTION d'un fichier INI.
Vous pouvez :

• Créer ou supprimer une SECTION,


• Créer ou supprimer un Mot Clé,
• Lire ou écrire pour un Mot Clé.

Attention, lors de la lecture ou de l'écriture pour un mot-clé, n'oubliez pas que le contenu des mots-
clés est toujours une chaîne…

N'omettez donc pas de passer aux fonctions INIEcrit() et INILit() une chaîne, et non pas une
valeur, sous peine d'erreur garantie, comme par exemple :

FicIni = FRepEncours()+''\DATA\PARAM.INI'' // Fichier autre que WIN.INI....

Syntaxe erronée :

CoulFond est un entier long=255


INIECRIT(''COULEURS'',''Fond'' ,CoulFond, FicIni)

La bonne syntaxe est :

INIECRIT(''COULEURS'',''Fond'' ,VersChaine(CoulFond), FicIni)

En lecture, c'est la même chose , n'oubliez pas de transformer le contenu du Mot Clé (qui est
toujours une chaîne..) avant de le réaffecter :

1ère syntaxe :

Coulfond=Val(INILIT(''COULEURS'',''Fond'' , '''' , FicIni))

Vous récupérez le contenu du Mot Clé Fond à la section COULEURS, mais si le Mot Clé est vide, la
variable CoulFond sera à 0 !

Il vaut donc mieux utiliser la syntaxe ci-dessous, qui permet d'affecter une valeur par défaut, si le
contenu du Mot Clé est vide :

2ème syntaxe :

Coulfond=Val(INILIT(''COULEURS'',''Fond'' , ''255'' , FicIni))

Vous pouvez directement écrire lors du code de fermeture d'une fenêtre, l'enregistrement des
couleurs des objets de cette fenêtre, comme par exemple :

INIECRIT(''COULEURS'', "Combo2",VersChaine(Combo2..CouleurFond),FicIni)

Ou
INIECRIT(''COULEURS'', "Champ",VersChaine(Champ..Couleur),FicIni)

Pour créer une nouvelle SECTION avec un nouveau Mot Clé :

INIECRIT(''NOUVELLE_SECTION'',''Nouveau_Mot Clé,'','',FicIni)

Pour supprimer un Mot Clé existant :


INIECRIT(''SECTION'',''MotCle'',Null, FicIni)

Pour supprimer une SECTION existante :

INIECRIT(''SECTION'',Null,Null, FicIni)

Dans la syntaxe des exemples ci-dessus, si ''FicIni'' n'est pas précisé, alors les opérations de
lecture et d'écriture ont lieu dans le fichier WIN.INI, ce qui signifie que sans précision du fichier à
utiliser, c'est WIN.INI qui sera utilisé.

Mais en plus de l'utilisation 'mécanique' des fichiers ini, il convient de réfléchir à une gestion globale
cohérente de ceux-ci…

Comment choisir entre un fichier ini propre à l'application ou partagé entre plusieurs applications,
voire même un fichier ini du système (win.ini, system.ini…) ?

Quelles sont les limites de l'utilisation des fichiers ini ? C'est ce que nous allons voir maintenant…

Limite des 64 Ko

Tout d'abord, il faut considérer la limite des 64 Ko des fichiers ini… Si chaque application présente
sur un ordinateur écrit ses paramètres dans win.ini, il est clair que celui-ci sera vite saturé.

Donc, le premier principe, qui parait évident mais n'est malheureusement pas toujours suivi par les
développeurs, c'est de ne placer dans les fichiers ini du système que ce qui est strictement
indispensable (par exemple un nouveau périphérique d'impression, l'installation d'un driver particulier
propre à votre application…).

Ces cas sont relativement rares. Dans la majorité des cas, il faut donc préférer un fichier ini propre à
votre application.

Ou placer un fichier ini ?

Ou placer un fichier ini ? Dans le répertoire Windows, dans celui de l'application ?

Dans le cas (rare) ou vous devez partager des informations entre plusieurs applications installées
dans des répertoires différents (ou mettre des informations à disposition d'applications externes), il
est clair que placer le fichier ini dans le répertoire Windows est une solution simple pour que chacun
puisse y accéder.

Par contre, dans la majorité des cas, les paramètres à inscrire dans le fichier ini sont propres à
l'application. Dans ce cas, il est clair que placer le fichier ini dans le répertoire de l'application est
beaucoup plus pratique.
En cas de déplacement ou de désinstallation, il ne restera pas de fichier dans le répertoire de
Windows.

De plus, si jamais le répertoire Windows devait être supprimé et recréé (mais personne n'a jamais
besoin de réinstaller Windows, n'est ce pas ?), votre application ne sera pas perturbée, son fichier de
paramétrages étant intact.

Enfin, vous ne ferez pas parti de ces programmeurs qui font désespérément grossir le répertoire
système pour rien (n'oubliez pas que le temps de chargement de Windows dépend en partie de la
taille du répertoire Windows et de ses sous-répertoires).

Fichiers ini et programmation réseau

Dans le cas d'un programme réseau, les fichiers ini sont bien évidemment réservés aux paramètres
'locaux'.

Si par contre vous souhaitez enregistrer des paramètres de fonctionnement par utilisateur, et que
ceux ci sont susceptibles de travailler sur plusieurs ordinateurs, il est clair que l'utilisation d'un fichier
ini n'est plus vraiment adaptée.

Dans ce cas, préférez leur un fichier HyperFile situé sur le serveur de fichier… Ce système vous
permettra de conserver des séries de paramètres communs à tous les utilisateurs ou propre à chaque
utilisateur mais utilisables depuis n'importe quel poste du réseau.

Pour vous faciliter la tache, voici deux procédures ParamLit et ParamEcrit construites avec
pratiquement la même syntaxe qu'Inilit et IniEcrit mais travaillant sur un fichier paramètre.

Tout d'abord, la structure du fichier paramètre :

Nom des zones du fichier param Type Lg.


PACLEUNIK Entier Long 4
Section Texte 30
Ligne Texte 30
ValeurTexte Texte 30
ValeurNum Entier Long 4
ValeurMemo Mémo Texte 4
ValeurMonetaire Monetaire 10
ValeurBinaire Mémo binaire 4
UnikKey=Section+Ligne Clé Composée unique 60

Cette structure comporte plus de champs qu'il n'est nécessaire pour gérer simplement l'équivalence
avec les IniLit et IniEcrit, mais puisqu'on gère un fichier de paramètres, autant augmenter la
puissance de ces fonctions.

Si vous désirez remplacer vos IniLit et IniEcrit existant par des appels à ParamLit et ParamEcrit avec
un minimum de travail, il vous suffit de faire deux remplacements successifs dans tout votre projet
(pour chaque fonction) :
- D'abord remplacer IniLit par ParamLit (ou IniEcrit par ParamEcrit)

- Puis remplacer NomDuFichierIni par la chaine "ValeurTexte", puisque les lectures/écritures


d'origines dans les fichiers ini n'acceptaient que des valeurs textes.

Voici donc les fonctions que vous pouvez intégrer dans vos sources, et tout d'abord, la fonction de
lecture de paramètre.

Procédure ParamLit(Section,Ligne,ValeurInit,NomDuChampDuFichier="TextValue")
//Cette procédure remplace IniLit pour conserver des informations dans
//une table hyperfile qui sera accessible via le réseau
//(Même information pour tous les utilisateurs ou différente pour chaque
//utilisateur (en mettant le nom d'utilisateur dans le champ 'Section') mais
//accessible sur n'importe quel point du réseau
//Le paramètre 'Section' peut contenir le nom d'utilisateur, ou "Fenêtre'...
//Le paramètre 'Ligne' peut contenir la clé secondaire, comme par exemple 'Position Hor'
//La clé unique est composée de Section+Ligne

//La procédure lit l'enregistrement dont la clé est Section+Ligne et


//renvoie la valeur trouvée dans le champ du fichier NomDuChampDuFichier.
//Le paramètre NomDuChampDuFichier contient le nom exact de la
//zone fichier (utilisation de l'indirection pour accéder au contenu)
//et détermine donc aussi le type de valeur renvoyée
//Le NomDuChampDuFichier peut être : "ValeurTexte", "ValeurNum",
//"ValeurMemo", "ValeurMonetaire" ou "ValeurBinaire"

TrSection est une chaîne=Section //Transforme la valeur qui peut


//arriver en numérique en une chaîne
TrSection=Majuscule(TrSection) //Transforme la chaîne qui peut contenir
//des minuscules en majuscules. Ceci permet
//de ne pas se préoccuper de la casse lors des
//appels à ParamLit et ParamEcrit, comme
//c'était le cas pour IniLit et IniEcrit
TrLigne est une chaîne=Ligne
TrLigne=Majuscule(TrLigne) //Comme pour la section

//Recherche de la ligne dans le fichier


HLitRecherche("Param", "UnikKey", Complete(TrSection,Dimension(Param.Section))+…
complete(TrLigne,Dimension(ParamsLigne)))
Tantque HdejaBloque()
HLitRecherche("Param", "UnikKey", Complete(TrSection, …
Dimension(Params.Section))+ complete(TrLigne,Dimension(Params.Ligne)))
Fin

//Si on ne trouve pas la ligne, on renvoi le valeur init passée en paramètre


Si pas h.trouve alors
Renvoyer ValeurInit
Sinon
//Si on travaille sur une valeur de type texte, on enlève les espaces
//ajoutés automatiquement dans les champs de fichiers HyperFile
Si NomDuChampDuFichier="ValeurTexte" ou …
NomDuChampDuFichier="ValeurMemo" alors
Renvoyer sansespace({"Param."+NomDuChampDuFichier})
Sinon
Renvoyer {"Param."+NomDuChampDuFichier}
Fin
Fin

Puis la fonction d'écriture de paramètre :

Procédure ParamEcrit(Section,Ligne,ValeurAEcrire,ChampDuFichier="ValeurTexte")
//Cette procédure remplace IniEcrit quand vous voulez enregistrer des informations
//dans une table HyperFile qui sera accessible via le réseau
//(Même information pour tous les utilisateurs ou différente pour chaque
//utilisateur (en mettant le nom d'utilisateur dans le champ 'Section') mais
//accessible sur n'importe quel point du réseau
//Le paramètre 'Section' peut contenir le nom d'utilisateur, ou "Fenêtre'...
//Le paramètre 'Ligne' peut contenir la clé secondaire, comme par exemple 'Position Hor'
//La clé unique est composée de Section+Ligne

//La procédure écrit l'enregistrement dont la clé est Section+Ligne et


//place la valeur dans le champ du fichier ChampDuFichier.
//Le paramètre ChampDuFichier contient le nom exact de la
//zone fichier (utilisation de l'indirection pour accéder au contenu)
//et détermine donc aussi le type de valeur
//Le ChampDuFichier peut être : "ValeurTexte", "ValeurNum",
//"ValeurMemo", "ValeurMonetaire" ou "ValeurBinaire"

//Exemple : ParamEcrit("GENERAL","IntroFacture",IntroFacture,"ValeurMemo")
// Section (ici les paramètres généraux à l'application)
// Ligne (le texte d'introduction des factures)
// La chaine contenant La valeur à écrire
// Sera placé dans le mémo texte

//Tout d'abord, on traite les paramètres en entrée


TrSection est une chaîne=Section //Transforme la valeur qui peut
//arriver en numérique en une chaîne
TrSection=Majuscule(TrSection) //Transforme la chaîne qui peut contenir
//des minuscules en majuscules. Ceci permet
//de ne pas se préoccuper de la casse lors des
//appels à ParamLit et ParamEcrit, comme
//c'était le cas pour IniLit et IniEcrit
TrLigne est une chaîne=Ligne //Même chose
TrLigne=Majuscule(TrLigne) //que pour la section

// Puis, on lit l'enregistrement et on le bloque


HLitRechercheBloque("Param", "UnikKey", Complete(TrSection, …
Dimension(Param.Section))+ complete(TrLigne,Dimension(Param.Ligne)))
Tantque hdejabloqué()
HLitRechercheBloque("Param","UnikKey", Complete(TrSection,…
Dimension(Params.Section))+ complete(TrLigne,Dimension(Params.Ligne)))
Fin
Si pas H.trouve alors
//L'enregistrement n'existe pas, on le créé
hRaz("Param")
Param.Section=Complete(TrSection,Dimension(Param.Section))
Param.Ligne=Complete(TrLigne,Dimension(Param.Ligne))
{"Param."+ChampDuFichier}=ValeurAEcrire
Hajoute("Param")
Sinon //L'enregistrement existe, on le modifie
{"Param."+NomDuChamp}=ValeurAEcrire
hmodifie("Param") //Débloque l'enregistrement par la même occasion
Fin

Fonctions supplémentaires

En plus de simplement remplacer la gestion des fichiers ini en cas de programme réseau, l'utilisation
des zones ValeurNum, ValeurMemo, ValeurMonetaire et ValeurBinaire vous permet de stocker
énormément de choses dans vos fichiers paramètres, comme des fichiers curseurs ou des fichiers
sons que vous extrairez très simplement au moment ou votre application en a besoin…

Les fonds de pages, logos et autres enrichissements de vos application trouvent ainsi une place bien
à l'abri des manipulations hasardeuses et située à une instruction (binairesauve/binairecharge) du
disque dur "classique"… Si vous ne les utilisez pas encore, testez donc la vitesse et la puissance de
cette instruction qui vous surprendra…

Ces possibilités supplémentaires vous permettent de décupler la puissance habituelle des fichiers ini,
en augmentant par exemple la sécurité de vos applications, puisqu'il n'est plus question qu'un
utilisateur ait supprimé les fichiers curseurs ou autres qui traînaient dans un sous-répertoire de votre
application… Vous les extrairez à la volée ou en début de programme et serez ainsi à peu prés
certain de disposer de ce dont vous avez besoin au moment opportun.

Augmentation des possibilités de vos programmes ? Augmentation de la sécurité ? N'hésitez plus,


paramétrez ☺ !

oOoOoOoOoOoOoOo
Fichiers REP

Tout d'abord, qu'est ce qu'un fichier "REP" ?

En fait, il s'agit d'un fichier texte que WinDev utilise pour stocker des informations sur l'emplacement
des fichiers HyperFile utilisés. La gestion des fichier "REP" étant optionnelle (hGereRepNon), il est
important de connaître les tenants et aboutissants de ces fichiers pour décider en connaissance de
cause si on souhaite de les utiliser ou pas, et si c'est nécessaire...

Ces fichiers portent normalement le nom de votre analyse et sont situés dans le répertoire HSF du
disque où est installé votre programme, sauf bien sûr si vous avez choisi un autre emplacement par
programmation.

Chaque fois que vous créez un fichier HyperFile, les caractéristiques de celui-ci sont enregistrées
dans le fichier "REP" (et en particulier son emplacement physique).

Ces informations seront nécessaires pour le bon fonctionnement de différents utilitaires PCSoft, et en
particulier le module de modification automatique des fichiers de données, lorsque vous avez effectué
des modifications dans l'analyse de votre projet.

Quand vous lancez une modification automatique des fichiers de données, l'utilitaire WdModfic
recherche le fichier "REP" pour savoir où se trouvent les fichiers à modifier. S'il ne trouve pas le
fichier "REP", il suppose que les fichiers se trouvent dans le répertoire défini dans l'analyse.

Donc, le choix est clair :

Si vous voulez pouvoir modifier la structure de vos fichiers en automatique et que


l'emplacement de ces fichiers est paramétrable (en particulier en fonctionnement réseau), il
est obligatoire que le fichier "REP" soit correctement renseigné.

Dans certains cas, il se peut que votre fichier "REP" ne contienne pas tous les emplacements de
fichiers à modifier, et vous pouvez même être amené à modifier manuellement ces informations.

Avant d'en arriver à cette extrémité, n'oubliez pas qu'un module de WdOutil.exe permet justement de
modifier les fichiers "REP", de vérifier la validité des informations qu'ils contiennent, et même de
rajouter des lignes…

Si vous tenez quand même à effectuer les modifications à la main, voici la structure des fichiers
"REP" : ils sont constitués de lignes séparées par des RC/LF. Chaque ligne a une longueur de 117
caractères sans les RC/LF, soit 119 caractères pour une ligne complète.

La structure d'une ligne est :

• 2 espaces,
• Le nom logique du fichier sur 8 caractères,
• 1 espace,
• Le nom physique COURT du fichier (si le fichier physique s'appelle "MonFichier", on aura ici
quelque chose comme MonFic~1) sur 8 caractères,
• 1 espace,
• Le chemin (ou tout au moins le début de celui-ci) tel que déclaré dans l'analyse sur 30
caractères. Cette zone est vide si on déclare les fichiers comme étant dans le répertoire
courant du programme. De plus, cette zone est codée en utilisant les noms de dossiers longs
(C:\PROGRAM FILES\MONPROGRAMME\…), ce qui fait que dès que les 30 caractères sont
atteints, l'information est incomplète… Donc, préférez les noms/chemins courts,
• 1 espace,
• Le chemin courant du fichier sur 64 caractères… Ici, le chemin est inscrit en utilisant les noms
courts, par exemple C:\PROGRA~1\MONPRO~1\… avec le "\" final… Malgré tout, la place
disponible n'est que de 64 caractères, donc bien inférieure à la taille possible d'un nom de
dossier. Evitez donc les emplacements de fichiers un peu trop "exotiques" si vous souhaitez
que tout fonctionne comme il faut,
• 1 espace
• 1 point d'exclamation
• LE RC/LF.

Par défaut, la gestion du fichier "REP" est active…

Donc sans rien faire de particulier, lorsque vous assignez / créez vos fichiers au début de votre projet
(ou lorsque le RAD le fait pour vous), tout est automatiquement mis en place pour que la modification
automatique des fichiers de données fonctionne parfaitement.

Dans la très grande majorité des cas, vous n'aurez donc jamais à mettre les mains dans un fichier
"REP".

Sachez aussi qu'un programme n'utilisant ni les journaux (HGereJnlNon), ni le fichier "REP"
(HGereRepNon), ni les transactions (HGereTrsNon) peut voir sa vitesse d'exécution augmenter de
10% en mono poste et 25% en réseau (extrait de l'aide en ligne de WinDev).

Donc vous pouvez améliorer les performances de votre programme, et si vous voulez quand même
profiter du système de modification automatique des fichiers, il ne vous reste qu'à installer aussi votre
programme sur le serveur de fichier.

Les fichiers doivent alors être placés dans le répertoire décrit dans l'analyse (je vous conseille le
répertoire courant du programme). La modification des données se fera alors sur le serveur, lors de
l'installation de la nouvelle version.

Si vous travaillez en réseau, sachez que la gestion du fichier "REP" ne supporte pas la notation
UMC (c'est le nom du système d'appellation des répertoires réseau qui permet, au lieu de devoir
assigner une lettre à un lecteur, de les atteindre grâce à un nom composé du nom du serveur
précédé de "\\" suivi du nom du répertoire). Ainsi, vous pouvez parfaitement assigner un fichier (ou
tous) dans un répertoire s'appelant \\NomServeur\NomRepertoire\ mais le fichier "REP"
n'enregistrera pas cet emplacement dans ce cas.

Pour travailler en réseau avec la gestion du fichier "REP" activée, il faut donc assigner une lettre (par
exemple "R:") au disque de la machine "\\NomServeur" dans l'explorateur WinDows et assigner le
fichier au répertoire "R:\NomRepertoire". Lors de la création des fichiers, R:\NomRepertoire sera donc
enregistré dans le fichier "REP". Il conviendra bien sûr que cette lettre soit assignée automatiquement
au disque réseau à chaque démarrage de la machine.
oOoOoOoOoOoOo
Fichiers REP

Tout d'abord, qu'est ce qu'un fichier "REP" ?

En fait, il s'agit d'un fichier texte que WinDev utilise pour stocker des informations sur l'emplacement
des fichiers HyperFile utilisés. La gestion des fichier "REP" étant optionnelle (hGereRepNon), il est
important de connaître les tenants et aboutissants de ces fichiers pour décider en connaissance de
cause si on souhaite de les utiliser ou pas, et si c'est nécessaire...

Ces fichiers portent normalement le nom de votre analyse et sont situés dans le répertoire HSF du
disque où est installé votre programme, sauf bien sûr si vous avez choisi un autre emplacement par
programmation.

Chaque fois que vous créez un fichier HyperFile, les caractéristiques de celui-ci sont enregistrées
dans le fichier "REP" (et en particulier son emplacement physique).

Ces informations seront nécessaires pour le bon fonctionnement de différents utilitaires PCSoft, et en
particulier le module de modification automatique des fichiers de données, lorsque vous avez effectué
des modifications dans l'analyse de votre projet.

Quand vous lancez une modification automatique des fichiers de données, l'utilitaire WdModfic
recherche le fichier "REP" pour savoir où se trouvent les fichiers à modifier. S'il ne trouve pas le
fichier "REP", il suppose que les fichiers se trouvent dans le répertoire défini dans l'analyse.

Donc, le choix est clair :

Si vous voulez pouvoir modifier la structure de vos fichiers en automatique et que


l'emplacement de ces fichiers est paramétrable (en particulier en fonctionnement réseau), il
est obligatoire que le fichier "REP" soit correctement renseigné.

Dans certains cas, il se peut que votre fichier "REP" ne contienne pas tous les emplacements de
fichiers à modifier, et vous pouvez même être amené à modifier manuellement ces informations.

Avant d'en arriver à cette extrémité, n'oubliez pas qu'un module de WdOutil.exe permet justement de
modifier les fichiers "REP", de vérifier la validité des informations qu'ils contiennent, et même de
rajouter des lignes…

Si vous tenez quand même à effectuer les modifications à la main, voici la structure des fichiers
"REP" : ils sont constitués de lignes séparées par des RC/LF. Chaque ligne a une longueur de 117
caractères sans les RC/LF, soit 119 caractères pour une ligne complète.

La structure d'une ligne est :

• 2 espaces,
• Le nom logique du fichier sur 8 caractères,
• 1 espace,
• Le nom physique COURT du fichier (si le fichier physique s'appelle "MonFichier", on aura ici
quelque chose comme MonFic~1) sur 8 caractères,
• 1 espace,
• Le chemin (ou tout au moins le début de celui-ci) tel que déclaré dans l'analyse sur 30
caractères. Cette zone est vide si on déclare les fichiers comme étant dans le répertoire
courant du programme. De plus, cette zone est codée en utilisant les noms de dossiers longs
(C:\PROGRAM FILES\MONPROGRAMME\…), ce qui fait que dès que les 30 caractères sont
atteints, l'information est incomplète… Donc, préférez les noms/chemins courts,
• 1 espace,
• Le chemin courant du fichier sur 64 caractères… Ici, le chemin est inscrit en utilisant les noms
courts, par exemple C:\PROGRA~1\MONPRO~1\… avec le "\" final… Malgré tout, la place
disponible n'est que de 64 caractères, donc bien inférieure à la taille possible d'un nom de
dossier. Evitez donc les emplacements de fichiers un peu trop "exotiques" si vous souhaitez
que tout fonctionne comme il faut,
• 1 espace
• 1 point d'exclamation
• LE RC/LF.

Par défaut, la gestion du fichier "REP" est active…

Donc sans rien faire de particulier, lorsque vous assignez / créez vos fichiers au début de votre projet
(ou lorsque le RAD le fait pour vous), tout est automatiquement mis en place pour que la modification
automatique des fichiers de données fonctionne parfaitement.

Dans la très grande majorité des cas, vous n'aurez donc jamais à mettre les mains dans un fichier
"REP".

Sachez aussi qu'un programme n'utilisant ni les journaux (HGereJnlNon), ni le fichier "REP"
(HGereRepNon), ni les transactions (HGereTrsNon) peut voir sa vitesse d'exécution augmenter de
10% en mono poste et 25% en réseau (extrait de l'aide en ligne de WinDev).

Donc vous pouvez améliorer les performances de votre programme, et si vous voulez quand même
profiter du système de modification automatique des fichiers, il ne vous reste qu'à installer aussi votre
programme sur le serveur de fichier.

Les fichiers doivent alors être placés dans le répertoire décrit dans l'analyse (je vous conseille le
répertoire courant du programme). La modification des données se fera alors sur le serveur, lors de
l'installation de la nouvelle version.

Si vous travaillez en réseau, sachez que la gestion du fichier "REP" ne supporte pas la notation
UMC (c'est le nom du système d'appellation des répertoires réseau qui permet, au lieu de devoir
assigner une lettre à un lecteur, de les atteindre grâce à un nom composé du nom du serveur
précédé de "\\" suivi du nom du répertoire). Ainsi, vous pouvez parfaitement assigner un fichier (ou
tous) dans un répertoire s'appelant \\NomServeur\NomRepertoire\ mais le fichier "REP"
n'enregistrera pas cet emplacement dans ce cas.

Pour travailler en réseau avec la gestion du fichier "REP" activée, il faut donc assigner une lettre (par
exemple "R:") au disque de la machine "\\NomServeur" dans l'explorateur WinDows et assigner le
fichier au répertoire "R:\NomRepertoire". Lors de la création des fichiers, R:\NomRepertoire sera donc
enregistré dans le fichier "REP". Il conviendra bien sûr que cette lettre soit assignée automatiquement
au disque réseau à chaque démarrage de la machine.
oOoOoOoOoOoOo
Bibliothèques WDL
Lors de la phase de création de l'exécutable, WinDev propose de mystérieuses options concernant
les "bibliothèques"…

Tout d'abord qu'est ce qu'une bibliothèque au sens WinDev ?

Une bibliothèque est un fichier dont l'extension est "wdl". Ce fichier est généré lors de la création de
l'exécutable et va contenir jusqu'à 1000 objets dont l'exécutable aura besoin…

Ces objets peuvent être :

• Des fenêtres,

• Des images,

• Des états…

Donc, quand vous créez une fenêtre dans un de vos projet et que cette fenêtre contient tout un tas
d'images (sur les boutons, en fond de fenêtre, en icône dans la barre de titre, etc…) vous ne stockez
pas UN objet dans la bibliothèque mais "N" objets.

Interne, Externe ou pas du tout

Au moment de la création de l'exécutable, WinDev vous propose 3 options concernant les


bibliothèques :

• Pas d'utilisation de bibliothèque

• Utilisation de la bibliothèque externe

• Intégration de la bibliothèque dans l'exécutable

Si vous choisissez la première option, aucune intégration des objets n'est effectuée… Vous vous
retrouvez donc avec un exécutable tout petit (puisqu'il ne s'agit plus en fait que d'un "lanceur") mais
vous devez fournir toutes les fenêtres, les images et autres ressources nécessaires, en même temps
que votre exécutable, chaque fenêtre ou état contenant tout son code source…

Pour d'évidentes raisons de sécurité, cette option ne devrait être utilisée qu'en cas de développement
interne à une entreprise, et encore… Le seul avantage qu'elle présente est de pouvoir remplacer
UNE FENETRE (par exemple) seule, sans changer l'exécutable ni quoi que ce soit d'autre… Vous
avez donc ici une flexibilité de mise à jour très grande.

En choisissant l'option "bibliothèque externe", WinDev créé le fichier WDL regroupant tous les objets
du projet… Ce fichier devra être fourni à vos clients en même temps que l'exécutable…

Quel avantage par rapport à l'intégration dans l'exécutable ? Certains vous diront que de ce fait vous
pouvez ne mettre à jour que la wdl chez vos clients (mais comme l'exécutable seul ne pèse que
quelques dizaines de Ko, le gain est négligeable).

Par contre, l'aide en ligne de WinDev indique que "Attention : pour les exécutables fonctionnant en
réseau, la bibliothèque doit être externe." Notre expérience nous indique qu'il faut comprendre ici que
les exécutables placés sur le serveur et appelés depuis les postes clients doivent être séparés de la
WDL.

Or, cette configuration est à éviter au maximum si vous ne voulez pas dégrader inutilement les
performances de votre réseau… Ce que nous avons appris au moment où les réseaux étaient lents
est toujours aussi vrai maintenant : "un réseau bien configuré est un réseau qui n'est pas utilisé" ☺…

Quand vous "pensez" réseau, il faut effet faire en sorte que les programmes et les données soient
placés de telle manière que l'utilisation du réseau soit minimisée… Faire transiter toutes les fenêtres,
les images, les icônes, les sons… par le réseau alors que le programme peut être installé sur tous les
postes est donc une aberration…

Au pire, si vous avez 500 postes à installer, un système d'installation automatique des nouvelles
versions intégré à votre exécutable résoudra le problème.

1000 objets

Revenons sur la limite des 1000 objets dans une bibliothèque. Il s'agit là d'une limite due à la
structure des fichiers WDL actuels. Vous pouvez donc un jour vous retrouver confronté à un problème
si votre projet a vraiment trop grossi !

Que faire dans ce cas ? Tout simplement, séparer votre projet en plusieurs sous-projets ou utiliser
plusieurs bibliothèques WDL…

Utilisation de plusieurs WDL

Avec les instructions ChargeWDL et DechargeWDL, WinDev nous fournit un moyen pour utiliser
jusqu'à 10 bibliothèques différentes simultanément. La bibliothèque principale du projet ne pouvant
être déchargée, il nous reste 9 bibliothèques "amovibles" avec lesquelles travailler.

A vous de séparer en modules "logiques" les différents objets (par exemple un module vente, un
module achat, un module compta…)

Mais un certain nombre de contraintes sont à prendre en compte avant de choisir une organisation de
ce genre :
• Vous devrez gérer le contenu des bibliothèques à la main ! En effet, si WinDev peut se
charger de générer une bibliothèque contenant tous les objets du projet, il n'offre aucun outil
(en tout cas dans sa version actuelle) pour affecter les fenêtres et autres objets à telle ou telle
bibliothèque…

• Vous ne devez pas utiliser de noms longs pour les objets placés dans des bibliothèques
secondaires, sous peine de comportements erratiques.

Si vous ne voulez pas de ces contraintes, envisagez plutôt de créer plusieurs projets regroupant des
fonctionnalités logiques… Un projet principal se charge des paramétrages et des fichiers de base
tandis que des projets secondaires contiennent des unités de traitements logiques…

Depuis votre projet principal, vous serez donc amenés à appeler des exécutables secondaires, ce qui
pour l'utilisateur sera totalement transparent.

Avec cette méthode, plus besoin de gérer des listes d'objets à la main, ni de vous restreindre aux
noms courts… Par contre, quand vous modifierez votre analyse (commune à tous les projets), vous
devrez mettre à jour et recompiler tous les sous-projets…

A vous donc de choisir la méthode de travail qui vous paraît la plus appropriée à vos besoins ou
contraintes…

Mise à jour partielle de programme

Nous avons vu qu'il était possible de séparer les objets du projet en plusieurs bibliothèques.

Cette fonctionnalité permet par exemple de faire des mises à jour partielles chez vos clients, en
n'envoyant que la ou les wdl modifiées.

Tout en considérant que cette mise à jour partielle serait tout aussi possible en gérant plusieurs
exécutables, voyons comment faire mieux…

Chaque WDL va en général être relativement grosse (parfois plusieurs Mo) et l'amélioration de
vitesse d'envoi restera donc limité… Pourquoi, dans ce cas, ne pas envoyer seulement les fenêtres
modifiés (ou les états) :

• Parce que WinDev charge en priorité l'objet contenu dans sa bibliothèque,

• Parce que vous envoyez alors en même temps les sources de votre objet.

Le premier problème peut être résolu en remplaçant les fonctions Ouvre, OuvreFille et OuvreSoeur
par des procédures globales qui se chargeront de tester si des fenêtres de remplacement
renommées se trouvent dans le répertoire de l'application (ou dans un sous répertoire)…

Ainsi, si vous utilisez dans votre application une fenêtre Client.wdw, vous pouvez fournir à vos client
une fenêtre m_Client.wdw corrigée et améliorée… La procédure globale d'ouverture fera donc les
traitements suivants :

• Ajout de "m_" au nom de la fenêtre à ouvrir,

• Test de la présence de cette fenêtre dans le répertoire des Mise à jour,


• Si elle y est, ouverture de RepMAJ\m_NomFen.wdw,

• Sinon, ouverture de NomFen.wdw.

Le problème de sécurité des sources peut être résolu de deux manières :

• Vous considérez que les sources d'une ou deux fenêtres ne sont pas vraiment un problème et
ne servent à rien sans le reste des sources de l'application et de l'analyse et vous placez dans
votre programme de quoi supprimer automatiquement les fenêtres de mise à jour quand vous
faîtes une mise à jour majeure.

• Vous supprimez les sources de vos fenêtres ou états avant de les envoyer… Pour cela, deux
méthodes :

o Vous générez un exécutable en supprimant les sources sur votre machine de


développement et vous extrayez ensuite la fenêtre sans son source grâce à un des
utilitaires présents sur le site de WinDevAsso

o Vous utilisez le tout nouvel outil écrit par Ugo Gennuso (en phase de test au moment
où ces lignes sont écrites) et qui permet justement de supprimer les sources d'un objet
WinDev isolé, et donc, de faire des mises à jour beaucoup plus sûres.

Bien sûr, dans les deux cas, vous prenez vos responsabilités car ces manipulations ne sont pas
dans le manuel WinDev… Personne ne peut donc vous certifier que tout se passera sans
problème !

Voilà, vous savez tout sur les wdl, ou presque… Mais quelques tests personnels vous apprendront
sûrement ce que vous ne savez pas encore à ce sujet important…

oOoOoOoOoOoOoO
Utilisation du débogueur

Ca chapitre n'a pas pour but de vous apprendre à utiliser le débogueur, l'aide en ligne et la
documentation de WinDev faisant déjà cela très bien. Nous allons plutôt étudier ici les limites du
débogueur, les problèmes que l'on peut rencontrer en l'utilisant, et voir comment optimiser son
utilisation

Le débogueur est la plupart du temps très largement suffisant pour trouver l'origine des problèmes
rencontrés, toutefois, son utilisation pose parfois des problèmes et nous allons en étudier quelques-
uns de plus prés…

Focus

Vous rencontrerez peut-être parfois des cas où votre code fonctionnera parfaitement sous le
débogueur, alors qu'il ne fonctionnera pas en mode test ou en exécution. Vous pouvez d'ailleurs
aussi rencontrer parfois le problème inverse…

Très souvent, genre de facétie se produit quand vous placez du code dans les zones "prise de
focus" et "perte de focus" de vos fenêtres.

Mais quand vous utilisez le débogueur, il faut penser que celui-ci ne va pas cesser de prendre le
focus et de le rendre aux fenêtres de votre application… L'ordre d'exécution de ces types de codes
est donc très fortement perturbé…

Vous pouvez donc avoir des effets de bandes comme :

• Un code n'a pas encore été exécuté alors que vous avez besoin du résultat qu'il produit,

• Un code a été exécuté plusieurs fois au lieu d'une seule et un cumul est faux…

Donc si vous avez besoin de placer du code dans ces zones, méfiance… Dans ce cas, l'utilisation de
Trace est parfois beaucoup plus efficace que le débogueur.

Timer

Les timers ne font pas non plus très bon ménage avec le débogueur :

• Ils perturbent l'exécution linéaire du code en déclenchant des procédures à tout bout de
champs (une procédure d'affichage de l'heure toutes les secondes est parfaite pour rendre
hystérique tout informaticien stressé ☺),

• Pour déboguer le code d'une procédure appelée par Timer, il faut remplacer les points d'arrêts
classiques par l'instruction "Stop".

Si le deuxième point ne pose pas de problème particulier (à part de s'en souvenir ☺), le premier peut
vous obliger à ne pas déclencher les timers quand vous êtes en mode test ou en mode de
déboguage…

Ceci peut être fait en utilisant le code suivant :

Si PAS EnModeTest() alors Timer(…

Ou encore, en ayant déclaré une variable globale booléenne EnModeDebug :

Si PAS EnModeDebug alors Timer(…

Dans ce dernier cas, avant de lancer votre séance de déboguage, il vous suffit de modifier votre
source pour mettre EnModeDebug à vrai. Vous pouvez même disposer d'une fonction cachée (qui
n'apparaît qu'en mode test) qui vous permet de modifier la valeur de cette variable de manière
dynamique, ce qui vous permet d'activer ou de désactiver les timers en fonction de vos besoins.

Visualisation de variables

Le débogueur affiche automatiquement le contenu des variables "courantes" (celles qui sont utilisées
dans le code en cours) dans une zone de visualisation. Les variables apparaissent et disparaissent
donc dans cette zone au gré du code exécuté.

Vous pouvez de plus demander à surveiller le contenu d'une ou de plusieurs variables même si elles
ne sont pas utilisées précisément à ce moment dans votre code. Cette pratique courante peut
toutefois vous réserver de mauvaises surprises.

En effet, le débogueur conserve la liste des variables en questions d'une exécution sur l'autre.
Vous pouvez donc vous retrouver dans le cas où il essaye d'afficher le contenu d'une variable qui
n'existe plus dans votre projet.

Vous vous retrouvez alors avec un message d'erreur qui apparaît à chaque "pas d'exécution"…

Si cela vous arrive, pensez à examiner la zone des variables et à supprimer ce qui y traîne encore.

Repousser les limites

La fonction permettant de visualiser et modifier le contenu des variables du débogueur est beaucoup
plus puissante qu'elle n'en a l'air…

En effet, en plus de pouvoir saisir le nom d'une variable locale, globale, fichier ou d'un champ d'une
fenêtre, vous pouvez aussi y saisir des expressions !

Ces expressions peuvent être l'appel à une fonction : Majuscule(SansEspace(MaVariable)) est ainsi
parfaitement accepté… Cet exemple peut vous permettre de vérifier comment est traité le contenu de
MaVariable dans une expression complexe dans le cas où cette variable contient des minuscules
accentués… Obtiendrez-vous des majuscules simples ou des majuscules accentuées ?

Mais ces expressions peuvent aussi être des TESTS ! Ainsi, si vous saisissez MaVariable=23, vous
obtiendrez 1 ou 0 selon que le test est vérifié ou pas… Inutile ? Ne croyez pas ça ! En cochant en
même temps la case "autostop", vous obtenez le moyen de lancer le code en fonctionnement normal
(bouton GO), tout en sachant qu'il s'arrêtera automatiquement dés que MaVariable atteindra la
valeur 23 (ou la quittera si elle valait 23 au départ)…

Inutile donc de passer 250 itérations d'une boucle en mode pas à pas… Commencez par
repérer grâce à un affichage quelle est la valeur qui pose problème, puis posez lui un piège
pour étudier en détail le code fautif, et uniquement lui !

Avec ces quelques informations importantes, vous êtes maintenant parés pour déboguer en toute
tranquillité vos merveilleux programmes.

oOoOoOoOoOoOoO
Programmation en réseau
Dans ce chapitre, nous allons étudier les subtilités de la programmation réseau… Il s'adresse donc à
tous ceux qui doivent faire le saut entre des applications mono-postes et des applications multi-
utilisateurs.

Au niveau de la programmation, le principe est le même si l'on se trouve dans une configuration
réseau ou multiposte, et même si on lance plusieurs occurrences d'un même programme sur un seul
poste.

Réseau ou Multiposte ?

Mais tout d'abord, voyons quelle est la différence entre réseau et multiposte.

Lorsque l'on parle d'une application fonctionnant en réseau, cela signifie que les différentes
occurrences d'un programme fonctionnent sur différentes unités centrales d'un même réseau
physique, chacune d'elle utilisant le processeur et la mémoire vive de SON unité centrale.

Par contre, lorsqu'un programme fonctionne dans un environnement multiposte, toutes les
occurrences du programme fonctionnent sur la même unité centrale, dans la même mémoire vive et
avec les ressources d'un seul processeur. Dans ce cas, seules les entrées/sorties sont déportées sur
d'autres machines, qu'il s'agisse de terminaux passifs ou d'ordinateurs faisant fonctionner un logiciel
d'émulation de terminal.

Dans le monde Windows, le meilleur exemple de multiposte est Windows NT TSE (Terminal Server
Edition)…

Pourquoi établir cette distinction entre deux modes de fonctionnements multi-utilisateurs ?

Tout d'abord pour vous rassurer ! En effet, 99% du code que vous écrirez fonctionnera de la même
manière dans les deux cas… Et le 1% qui reste ne concerne pas la programmation réseau à
proprement parler, mais plutôt le paramétrage du fonctionnement…

En effet, si en mode réseau on peut la plupart du temps se remettre à un fichier ini unique se
trouvant dans le répertoire de l'application pour gérer le paramétrage du fonctionnement, il est clair
que dans le cas du multiposte, ce n'est plus vrai, tous les utilisateurs partageant la même copie du
programme et donc le même répertoire…
Ce problème peut d'ailleurs aussi se poser en mode réseau, si l'application ne se trouve pas
physiquement sur le poste local mais sur un serveur d'applications.

Donc, pour programmer proprement le paramétrage de fonctionnement, il est clair qu'il faut privilégier
l'usage d'un fichier de paramétrage HyperFile par utilisateur, tel que décrit dans le chapitre des
fichiers INI, afin d'être sûr que le programme fonctionnera correctement quelle que soit la plate forme
matérielle.

Réseau / mono poste : les différences théoriques

Il est bien évident qu'au delà de la programmation du paramétrage de fonctionnement, il faut


maintenant s'attacher à la programmation des accès fichiers… Que se passe-t-il en environnement
multi-utilisateurs simultanés, puisqu'on a vu précédemment que le terme d'environnement réseau ne
recouvrait qu'une partie des cas rencontrés…

Le problème se pose bien évidemment au niveau de la gestion des fichiers de données, que ceux-ci
soient des fichiers HyperFile, xBase ou qu'il s'agisse d'une base SQL.

Dans notre cas, il faut prendre en compte les éventualités suivantes :

• Plusieurs utilisateurs peuvent accéder au même fichier simultanément

• Pire que cela, plusieurs utilisateurs peuvent accéder au même enregistrement d'un même
fichier simultanément

Bien sûr, si les utilisateurs ne faisaient que lire des enregistrements, il n'y aurait pas de problème…
Mais comme ils en modifient, en ajoutent et en suppriment, il faut mettre en place du code particulier
pour gérer ces éventualités.

Blocages

En fait, toute la programmation réseau repose sur le blocage de certains enregistrements et sur la
détection des enregistrements bloqués.

Il faut donc bien comprendre quand et combien de temps il faut bloquer un enregistrement ! Nous
allons donc étudier les différents types d'accès à un enregistrement ou à un fichier :

• Lecture : Quand on lit un enregistrement (pour l'afficher à l'écran ou pour l'imprimer), il est
clair qu'il est inutile de le bloquer
• Ajout : Quand on ajoute un enregistrement, il n'y a aucune notion de blocage,
l'enregistrement n'existant pas encore
• Modification : il est obligatoire de bloquer l'enregistrement avant de le modifier, de manière à
éviter que deux utilisateurs écrasent mutuellement leurs modifications
• Suppression : là encore, il faut bloquer l'enregistrement avant d'effectuer la suppression… Il
ne faut pas supprimer un enregistrement qui est en train d'être lu/modifié par quelqu'un
d'autre.

On a aussi la possibilité de bloquer un fichier en totalité, mais cette option ne devient nécessaire que
si on doit effectuer des traitements lourds sur celui ci :

• Réindexation

• Suppression/vidage du fichier

• Calculs nécessitant le parcours de la totalité ou d'une partie d'un fichier et nécessitant


plusieurs passages entre lesquels le contenu doit obligatoirement être identique…

Comme vous le voyez, il s'agit là de cas rares mais auxquels vous devrez quand même penser…

Résumons nous :

la programmation réseau consiste donc à bloquer certains enregistrements ou fichiers avant


d'effectuer certains traitements et, bien sûr, de vérifier TOUT LE TEMPS si les
enregistrements/fichiers auxquels on désire accéder sont déjà bloqués par un autre utilisateur.

L'objectif étant bien sûr de ne bloquer des enregistrement que lorsque cela devient vraiment
nécessaire, et uniquement le temps nécessaire.

Par exemple, si un utilisateur demande à modifier un enregistrement d'un fichier, votre programme va
tout d'abord l'afficher, puis laisser la main à l'utilisateur. Si votre programme a déjà bloqué
l'enregistrement à ce moment, la durée du blocage peut être TRES longue…

L'utilisateur peut être dérangé, voire même partir en catastrophe et laisser l'enregistrement bloqué
pendant des heures…

La bonne "stratégie" est donc de ne bloquer l'enregistrement que quand l'utilisateur clique sur le
bouton déclenchant l'enregistrement dans le fichier. A ce moment, le code doit suivre la logique
suivante :

• Vérification de la validité du contenu des zones de saisies, messages d'erreurs ou


d'avertissements et retours en saisie éventuels,

• Blocage de l'enregistrement fichier courant,

• Si l'enregistrement ne peut pas être bloqué, on peut, selon les cas :

o Afficher un message d'erreur et sortir

o Boucler jusqu'à que ce que l'enregistrement puisse être bloqué (risque de boucle
infinie)

o Avertir l'utilisateur et lui demander ce qu'il veut faire : réessayer, abandonner la


modification,…

o Mixer plusieurs des stratégies ci-dessus (par exemple, on essaye plusieurs fois avec
une attente entre chaque essai, et au bout de n essais, on avertit l'utilisateur, puis…)

• Une fois l'enregistrement bloqué, on affecte les zones de saisies aux zones fichiers

• On enregistre la modification

• Enfin on débloque l'enregistrement

Sur les machines actuelles, il est clair que le temps écoulé entre le blocage et le déblocage de
l'enregistrement se chiffre en millièmes de secondes, au pire en centièmes !

En appliquant une "stratégie réseau" correcte, on va donc diminuer très fortement le risque de conflits
entre utilisateurs.

Cette stratégie réseau peut se résumer en deux points :

1. Diminuer autant que possible le risque de conflits entre utilisateurs (diminution de la durée de
blocage par exemple)
2. Gérer ces risques de blocages sur TOUS LES ACCES aux fichiers, même si le risque est
minime.

Logique de fonctionnement

Examinons maintenant la logique de fonctionnement d'une application tout à fait courante : une
gestion commerciale. Le but de cet examen est de mettre en lumière les différents niveaux de
risques de conflits entre utilisateurs et de trouver des méthodes pour les réduire au minimum
possible.

1. Prenons tout d'abord l'exemple de la gestion de documents comme des devis ou des factures.

En toute logique, il est très improbable que deux utilisateurs veuillent simultanément modifier
un même devis ou une même facture, soit parce qu'une seule personne est chargée de cette
tâche (et a les autorisations pour cela), soit parce que chaque personne qui est chargée de
cette tâche ne s'occupe que de son groupe de clients.

Dans ce cas, on voit bien qu'une modification simultanée est logiquement très improbable…
On pourrait donc en toute logique envisager de se passer de la gestion des blocages…
Malheureusement, la logique et la vie réelle faisant rarement bon ménage, il faudra tout de
même prévoir ce cas improbable… Ici, il n'y a pas de moyen de diminuer le risque de conflit
qui est au niveau minimum.

Par contre, un point à considérer ici est "Que faut-il bloquer ?"… En effet, un document de ce
type est en général constitué d'informations provenant de deux fichiers :
• Une ligne d'entête/Pied de page dans un fichier principal
• 'n' lignes de devis ou de factures dans un fichier secondaire

Bloquer les lignes de détail (l'une après l'autre ou toutes ensembles) ne servirait à rien
puisqu'il suffit de bloquer la ligne d'entête pour que tout le document devienne inaccessible.
Bien sûr cela suppose que tous les traitements qui accèdent aux lignes du documents
accèdent d'abord à l'entête… Mais si la logique est respectée, cela doit être le cas !

2. Voyons maintenant ce qui se passe au niveau des fichiers "de base", comme par exemple le
fichier clients.

Là encore, il est clair que les utilisateurs vont très rarement modifier simultanément la même
fiche client. D'abord, parce que soit une seule personne est chargée de saisir les modifications
d'adresse ou de téléphone, soit chacun gère ses clients, ensuite parce que même si plusieurs
personnes veulent faire des modifications sur la même fiche, la fréquence de ces
modifications étant peu importante, il est très peu probable que les tentatives d'accès se
fassent au même moment.

3. Enfin, voyons ce qui se passe au niveau des informations de cumuls/statistiques. Dans ce


cas, il est clair que, selon la logique de programmation employée, on peut se trouver face à
des accès concurrentiels fréquents.

Ainsi, si chaque saisie de devis/facture met à jour des cumuls en temps réel, les compteur de
cumuls vont être très sollicités et les blocages vont s'avérer primordiaux.

Par contre, si les cumuls sont fait lors d'un traitement effectué soit la nuit, quand personne ne
travaille, soit à la demande sur une période précise, seul ce traitement accèdera en
modification aux zones de cumuls et les risques de conflits sont quasi inexistants.

Bien sûr, il faut aussi considérer l'endroit où ces zones de cumuls se trouvent…

Prenons le cas d'un compteur de chiffre d'affaires par client. Si ce compteur se trouve (assez
logiquement) dans l'enregistrement client, il est tout à fait possible que quelqu'un essaye de
modifier cette fiche pendant que le traitement de cumul est effectué sur un autre poste… Avec
cette configuration de fichiers, on augmente le risque de conflits.

Pour respecter notre stratégie réseau, il conviendra donc de gérer deux fichiers :

• Le fichier client principal


• Le fichier de cumuls par client

Comme vous le voyez la nécessité réelle de blocage est dépendante de la logique de


fonctionnement de l'application. Sans enlever en quoi que ce soit la nécessité de gérer les
blocages lors de tous les accès, on peut très fortement diminuer les cas d'accès concurrentiels en
séparant les informations dans des enregistrement "logiques", ce qui simplifiera très fortement les
schémas de fonctionnement.

C'est vrai, nous insistons beaucoup sur ce point ! Mais pour programmer "confortablement" en
réseau, il est très important de "penser réseau" ! Ce que vous devez toujours avoir en tête, c'est
que, en diminuant les probabilités de conflit d'accès sur un enregistrement, on diminue en même
temps :
• Les risques d'erreurs (dues à une erreur de programmation, ça c'est votre domaine)
• Les risques d'inconvénients pour l'utilisateur (enregistrement bloqué, message d'erreur ou
d'avertissement, traitement impossible… Cà, c'est la face utilisateur).

En pratique, avec HyperFile

Tout d'abord, voyons comment accéder aux fichiers… Ces fichiers sont partagés (autrement, ce n'est
pas de la programmation réseau ☺), donc ils doivent se trouver quelque part sur le réseau…

L'emplacement des fichiers n'est donc plus fixe, et il faut donc commencer par gérer un paramétrage
d'emplacement.

Typiquement, cela se fait dans un fichier ini (et nous vous conseillons de vous reporter au chapitre
qui leur est consacré si vous n'êtes pas familier de leur utilisation)…

Le code Wlangage suivant permet de gérer cette situation. Vous pouvez le placer dans le code
d'initialisation du projet, ou encore mieux, dans une procédure globale que vous pourrez appeler d'où
vous voulez dans votre programme, en plus de l'appeler depuis le code d'initialisation du projet…

Pourquoi cette astuce ? Parce que ce système vous permet aussi de gérer très simplement
plusieurs séries de fichiers situés à des emplacement différents, soit pour une gestion multi-sociétés,
soit pour une gestion multi-exercices, ceci sans rien changer aux possibilités d'accès réseau !

Tout le code de ce chapitre a été regroupé dans un projet ProgRes que vous pourrez installer
directement depuis l'interface du Guide : GDWProgRes

Ce projet utilise des fichiers générés automatiquement par WinDev (groupe carnet d'adresse) et des
fenêtres volontairement très simples pour illustrer les traitements réseau.

Donc, nous choisissons de placer ce code dans la procédure globale RADProjetInit (si vous utilisez
le RAD à n'importe quel moment, WinDev l'aura déjà créée, donc inutile d'en créer une autre, autant
tout regrouper sous ce nom standard. Dans ce code, TrRepFichiers et TrFifchierIni sont des variables
globales au projet. Vous trouverez leur déclaration et leur initialisation dans le projet d'exemple.

Procedure RadProjetInit()
// Cette procédure s'occupe de gérer l'initialisation du système de fichiers

// On commence par lire dans le fichier de paramétrage


TrRepFichiers=inilit("Général","Répertoire des fichiers","",TrFichierIni)
Si TrRepFichiers<>"" alors //// On l'a trouvé, mais...
// Vérifie si on peut accéder au répertoire en question
Si frep(TrRepFichiers,FrRepertoire)="" alors
bip
Si ouinon("Le répertoire "+sansespace(TrRepFichiers)+...
" est inaccessible ou inexistant ! Vous pouvez : ",...
"- En choisir un autre en répondant OUI",...
"- Sortir du programme en répondant NON")=Non alors
FinProgramme()
Fin
// Ici, on ouvre la fenêtre qui permet de choisir et d'enregistrer le répertoire
//pour les fichiers HyperFile
Ouvre("Param")

// Puis on vérifie si cette fois ci tout va bien


TrRepFichiers=inilit("Général","Répertoire des fichiers","",TrFichierIni)
Si TrRepFichiers<>"" alors
// Vérifie si on peut accéder au répertoire en question
Si frep(TrRepFichiers,FrRepertoire)="" alors
Erreur("Le répertoire des fichiers n'est toujours pas "+…
"accessible ! Exécution du programme abandonnée !")
finprogramme()
fin
fin
fin

// On a donc un nom de répertoire valide, on indique


// au système de fichiers que tous les fichiers HyperFile
// se trouvent dans ce répertoire
hsubstrep("?",TrRepFichiers)
fin

// Puis on met en place les options de fonctionnement du moteur HyperFile


// c'est ici que vous pouvez rajouter tout un tas d'options (réplication, mémos,...)
// en fonction de vos besoins
HModePerso(Vrai))
HGereIntégritéOui()

// Et enfin, on crée tous les fichiers s'ils n'existent pas


Hcreationsiinexistant("*")

Voilà, vous savez maintenant travailler sur des fichiers HyperFile dont l'emplacement est
quelconque… Voyons maintenant comment les utiliser !

Un code classique en réseau, pour ajouter un enregistrement dans un fichier, va être :

hAjoute(Fichier) // On essaye d'ajouter

tantque hdejabloque() // Comme on est en mode personnalisé, il faut tester, même


// si un blocage est impossible
hAjoute(Fichier)
FIN

si hdoublon() alors // Si on gère des clés uniques, il faut aussi vérifier


// Si la clé n'existe pas déjà
bip
info("Erreur de doublon en ajout d'enregistrement sur fichier "+Fichier+"!")
FIN

si herrintegrite() alors // Si on gère des liaisons entre fichiers, il faut aussi


// vérifier si il n'y a pas d'erreur d'intégrité
bip
info("Erreur d'intégrité en ajoute d'enregistrement sur fichier "+Fichier+"!")
FIN

Etudions maintenant les points intéressants de ce code :

1. On est en mode personnalisé : En effet, dans la fonction d'initialisation du système de


fichier, on a placé l'instruction HModePerso(vrai). Les instructions HModePerso,
HModeSemiPerso et HModeAuto permettent de choisir un mode de fonctionnement particulier
du moteur HyperFile, et ce point mérite qu'on s'y attarde. Bien sûr, vous trouverez beaucoup
d'informations dans l'aide en ligne de WinDev sur les subtilités de ces différents modes…
Nous n'allons ici que souligner des points très importants et vous indiquer NOS préférences :

• HModePerso(Vrai) : Dans ce mode, il faut tester HDejaBloque après chaque fonction


d'accès au fichier ou à un enregistrement du fichier, même non bloquante (extrait de l'aide
en ligne). A vous la plomberie sur TOUTES LES FONCTIONS D'ACCES AUX FICHIERS
!

• HModePerso(Faux) : Dans ce mode, les fonctions de lecture (HLit*) ne retournent aucune


erreur de blocage. Ainsi, il n'est pas nécessaire de tester HdejaBloque après chaque
fonction (extrait de l'aide en ligne). C'est déjà mieux ? Pas si sûr ! En effet, savez vous
précisément quelles sont les fonctions à tester et celles à ne pas tester ? Etes vous sûr de
ne jamais confondre et donc oublier des tests ?

• HModeSemiPerso() : Ce mode permet de faire gérer tous les problèmes d'accès


concurrentiels par le moteur HyperFile… Vous n'avez rien à faire de particulier, le moteur
bloque les enregistrements quand c'est nécessaire et se débrouille tout seul… Fantastique
? Pas tant que ça ! Si le moteur ne peut bloquer correctement un enregistrement, un
message va s'afficher et le programme pourra être interrompu. Si vous aviez des
traitements un peu complexes en cours, tant pis pour vous… Vous ne pouvez pas
reprendre la main en cas de problème, le moteur se débrouillant tout seul avec l'utilisateur.
Ce mode est donc à réserver uniquement aux applications TRES SIMPLES !

• HModeAuto() : uniquement pour les accès mono postes et les fichiers sur CD/ROM (avec
le paramètre FAUX)

Pourquoi avoir choisi le mode personnalisé avec le paramètre VRAI qui donne le plus de
travail à faire dans le code ? Nous avons vu-ci-dessus que HModeAuto et HModeSemiPerso
étaient à écarter pour toute application réseau conséquente. HModePerso s'imposait donc de
lui même.
Le choix du paramètre VRAI implique en principe plus de codage, mais permet d'éviter de se
poser des questions : le travail est à faire SYSTEMATIQUEMENT, pas d'ambiguïté ou d'erreur
possible !

Mais rassurez vous, nous allons vous livrer des trucs pour que le travail à effectuer se réduise
au minimum.

2. Après un Hajoute, on a en fait TROIS groupes de traitements à effectuer :


• Test de blocage et traitement éventuel
• Test d'erreur de doublon et traitement éventuel
• Test d'erreur d'intégrité et traitement éventuel

Beaucoup de tests et de codes pour une seule ligne d'instruction intéressante. Votre code va
donc rapidement ressembler à une véritable jungle…

Ce qui est vrai pour un hAjoute l'est aussi pour les autres instructions d'accès fichiers… A
chaque fois, il sera nécessaire de vérifier si on a trouvé l'enregistrement recherché, s'il n'est
pas bloqué, si …

3. Que se passe-t-il si on OUBLIE de tester le blocage ? Ce qui est très amusant dans ce cas,
c'est qu'il ne se passe rien ! En fait, il ne se passe rien tout de suite, mais par contre, lors de
l'accès suivant à un fichier, vous allez récolter une très jolie ERREUR 5, et ce même si sur
cet accès vous avez bien testé les blocages !!!

Ce mode de fonctionnement est très perturbant quand vous commencez à programmer en


réseau, car l'erreur survient sur du code correct, alors qu'à l'emplacement incorrect rien ne
s'est produit ! La recherche de l'erreur peut parfois prendre du temps !

Que faire pour résoudre tous ces problème en une seule fois ? Et bien, la solution est
l'encapsulation ! Pour ce faire, vous pouvez utiliser des procédure globales ou une classe, selon
votre goût… Nous avons choisi ici de vous fournir des procédures globales, afin que tous ceux qui ne
sont pas familiers avec l'utilisation des classes ne soient pas perturbés.

Mais qu'est ce que l'encapsulation ? Un exemple sera plus parlant qu'un long discours ! Voici donc la
procédure globale GDWHajoute extraite du projet d'exemple :

Procédure GDWhAjoute(Fichier,Commande="")
// Commande peut indiquer des variations de fonctionnement
// Si Commande contient :
// NOMESS : on ne fait pas les messages
// ...
Commande=majuscule(Commande)
TrBoolSv est un booleen
TrBoolSv=AffMessage // On sauvegarde AffMessage qui contient le mode
// de fonctionnement courant : Affichage des messages
// ou pas

Si position(Commande,"NOMESS")>0 alors
AffMessage=faux
FIN

TrResult est un booleen=Vrai

hAjoute(Fichier)
tantque hdejabloque()
hAjoute(Fichier)
FIN

si hdoublon() alors
bip
Si AffMessage alors info("Erreur de doublon en ajout "+…
"d'enregistrement sur fichier "+Fichier+"!")
TrResult=faux
FIN

si herrintegrite() alors
bip
Si AffMessage alors
Si AffMessage alors info("Erreur d'intégrité en ajout "+…
"d'enregistrement sur fichier "+Fichier+"!")
Fin
TrResult= faux
FIN

// On restaure l'état initial


AffMessage=TrBoolSv

renvoyer TrResult

Vous avez compris ? Dans votre code, il vous suffira de remplacer tous les hAjoute par des
GDWHajoute… Ceux ci-se chargent d'effectuer les tests de blocage et d'autres tests obligatoires…

Bien entendu, d'autres procédures (GDWHlitRecherche, GDWHlitSuivant, GDWHmodifie…)


effectueront le même travail en remplaçant les ordres fichiers "classiques"…

En faisant un rechercher/remplacer dans tout le projet des ordres standards par les ordres
"encapsulés", vous transformerez du code monoposte en code réseau très rapidement… Il vous
restera à ajouter avant certains traitement (modification, suppression), des ordres de blocage…

Examinez attentivement le code de la fenêtre "client" fournie dans le projet d'exemple et vous verrez
que le travail est très simple.
Les avantages de ce système sont nombreux :

• Vous pouvez appliquer le même traitement standard très rapidement et partout…

• Si vous avez des cas particuliers, vous pouvez ajouter des paramètres de fonctionnement à
vos procédures d'encapsulation, comme c'est le cas dans GDWHajoute, où on peut afficher
ou pas les messages d'erreurs

• En sortie de ces procédures, les flags classiques sont toujours actifs, vous pouvez donc tester
vous même hdoublon, h.errintegrite… et coder le traitement spécifique dont vous avez besoin
à certains endroits.

• Le jour où vous décidez de changer le comportement global de votre programme vis à vis des
blocages, vous ne devez le changer que dans les procédures globales, ce qui sera très
rapide. Dans nos exemples de procédures globales, nous partons du principe que les
enregistrements ne restent bloqués que très peu de temps… La boucle de test de blocage ne
comporte donc pas de sortie en time out…

Mais si vous pensez que, dans votre cas, le blocage peut durer longtemps, rien ne vous
empêche de rajouter un compteur du nombre d'itérations, un message demandant des
instructions, l'enregistrement de l'information dans un fichier de logs pour vous permettre
d'optimiser plus tard les accès réseaux…

Les fonctions fournies dans l'exemple sont simples mais opérationnelles… A vous de les enrichir en
fonction de vos besoins.

De plus, comme vous pouvez le voir, il n'y en a qu'un petit nombre si on le compare au nombre de
fonctions fichiers disponibles en Wlangage… Seules les fonctions essentielles ont été encapsulées
ici, mais en vous basant sur ces exemples, vous pourrez très facilement ajouter les fonctions qui
manqueraient pour VOS traitements.

Voilà, vous savez programmer en réseau, il ne vous manque plus qu'un peu de pratique ☺ !

oOoOoOoOoOoOo
Résolution des Problèmes sur fichiers HyperFile
Dans ce chapitre, nous allons essayer de trouver des solutions aux différents types de problèmes qui
se posent parfois sur les fichiers Hyperfile.

Nous allons donc étudier tout d'abord les causes des problèmes et comment y remédier, puis nous
allons vous fournir quelques solutions pour réparer des fichiers endommagés.

Les problèmes et leurs causes

Voici un extrait de l'aide en ligne de WinDev, dans la rubrique "Erreur:Fichiers au format Hyper
File"…

Ce sont ces cas d'erreurs que nous allons étudier ici… En étudiant la totalité de cette rubrique du
fichier d'aide, vous comprendrez que les autres cas peuvent être résolus assez simplement.

07: ERREUR DE LECTURE

Cette erreur intervient généralement lorsqu'il y a un problème sur disque (secteur endommagé)
ou un problème de lecture sur le réseau (problème temporaire réseau, réessayer) ou que le
numéro d'enregistrement à lire est nul.

08: ERREUR D'ECRITURE

Cette erreur intervient généralement lorsqu'il y a un problème sur disque (secteur endommagé)
ou un problème d'écriture sur le réseau (problème temporaire réseau, réessayer) ou que le
numéro d'enregistrement à écrire est nul.

11: CLE INCORRECTE

Lors d'une fonction HModifie, la clé constituée à partir de l'enregistrement en mémoire n'est pas
retrouvée dans l'index. Il s'agit soit d'une erreur de programmation (aucun enregistrement n'a été
lu) soit d'un index défectueux (le réindexer avec WD411NDX.EXE ou WDOutil).
H.ErrIgnore est sans effet pour cette erreur.

Quand ces erreurs surviennent lors d'un accès fichier, il faut intervenir rapidement… Les causes
peuvent être diverses, mais dans tous les cas, c'est un problème d'origine externe au programme
lui même, sauf bien entendu si vous essayer de lire ou d'écrire un enregistrement dont le numéro
est 0. Dans ce dernier cas, sachez que l'enregistrement 0 des fichiers hyperfile est un
enregistrement réservé pour le moteur hyperfile qui y stocke des informations particulières
(dernier n° d'identifiant automatique attribué par exemple).

Dans tous les autres cas, il convient de faire 2 choses : réparer les fichiers abîmés et, si possible,
trouver la cause du problème de manière à éviter que celui-ci ne se répète.

Pour réparer des fichiers abîmés, la manipulation de base est bien entendu la réindexation. Elle
peut se faire à partir de l'utilitaire fourni avec WinDev (WdOutil) ou directement dans votre
programme grâce à l'instruction hReindexe.

Malheureusement, il peut arriver différents cas où cette réindexation ne résout pas le problème.
Rassurez vous, il y a des solutions et nous vous les dévoilerons dans la deuxième partie de ce
chapitre.

Mais voyons maintenant les causes de ces problèmes… Ce type d'erreur ne provient pas d'une
erreur de programmation, donc, ne cherchez pas dans votre code… Bien sur, il y a des moyens
de diminuer les risques d'erreurs de ce genre, en fermant les fichiers inutilisés par exemple. Mais
ce n'est qu'un palliatif ! Vous diminuerez peut-être la fréquence des erreurs, mais vous ne les
résoudrez pas comme ça.

Il faut commencer par comprendre l'origine de ce type d'erreurs… Que ce soit en réseau ou en
mono poste, Windows se charge de gérer les accès aux disques durs et donc aux fichiers…
Lorsque des problèmes physiques ou logiques surviennent dans la gestion du disque, la gestion
du réseau ou tout simplement dans le noyau de Windows, il arrive que celui-ci ne puisse tout
simplement pas effectuer une écriture.

Quelles peuvent être les conséquences de cette écriture manquante :

• Au mieux, il manque un enregistrement dans le fichier, mais sa cohérence est intacte (le
problème s'est produit avant que les fichiers de données et d'index ne soient modifiés)

• Par contre, si les informations qui étaient en attente d'écriture étaient destinées au fichier
d'index alors que celle destinées au fichier de données ont déjà été écrites, les résultats
sont imprévisibles…

En effet le fichier d'index étant organisé en arbre binaire, vous pouvez avoir un
enregistrement qui a disparu (voire un groupe d'enregistrement ou la plus grande partie du
fichier).

Les informations sont bien dans le fichier de données, mais l'arbre qui décrit leur
ordonnancement ayant été abîmé, un nœud et tous ses sous-nœuds et feuilles a disparu.

Ce problème peut d'ailleurs se produire sans qu'aucun message d'erreur n'apparaisse…


Ceci arrive quand un nœud à disparu mais que la structure restante de l'index est
cohérente… En réindexant, les données réapparaissent comme par magie.

• Encore plus grave, il se peut que le CONTENU d'un enregistrement soit abîmé, soit que
Windows n'ait écrit qu'une partie de l'enregistrement, soit que le contenu de
l'enregistrement lui même ait été altéré… Ceci peut aussi bien arriver lors de l'écriture
dans le fichier d'index (le .ndx) que dans le fichier de données (le .fic) ou même dans le
fichier des mémos (le .mmo).

Dans ce dernier cas, si la réindexation permet de réparer un fichier d'index cassé (plus
précisément, permet de le recréer), elle ne peut rien pour réparer le contenu du fichier de
données ou du fichier mémo… Il faudra parfois avoir recours à d'autres stratégies qui
seront décrites dans la deuxième partie de ce chapitre.

Que faire pour éviter que le problème ne se reproduise ?

Examinons les différentes causes :

• Problème disque : il faudra reformater, changer le disque, ou toute autre solution analogue

• Problème électrique : une coupure de courant peut se reproduire, il faut donc envisager de
mettre en place un onduleur, ou de mettre à l'abri les câbles électriques qui ont été
arrachés, ou encore de remplacer l'alimentation de l'ordinateur…

• Problèmes réseau : il faut séparer deux types de problèmes très différents :

o Les problèmes physiques. Ces problèmes peuvent eux même être très variables.
Dans tous ces cas, le plus dur n'est pas de dépanner, mais de trouver la cause
précise du problème. Citons :

Un réseau 10 Mbit surchargé va occasionner de nombreuses collisions sur


le réseau… Le gestionnaire réseau de Windows étant ce qu'il est, ces
circonstances peuvent assez facilement dégénérer en erreur fichier. La
solution est simple, passez en réseau 100 Mbits.

Un câblage de type coaxial est souvent sensible aux perturbations


extérieures. Un bouchon de terminaison mal fixé, un câble réseau placé
trop près d'un câble de téléphone ou électrique, d'un néon ou d'un moteur
électrique (vive les frigos dans les bureaux)…

Des cartes réseaux de mauvaise qualité, ou tout simplement paramétrées


de manière différentes les unes des autres (half duplex/full duplex)

Un câblage mal réalisé, dont l'exemple typique est le câblage 100 Mbits en
RJ 45 réalisé par l'électricien du quartier. Comme ce câblage est droit (1-1,
2-2…8-8), il est souvent réalisé n'importe comment, alors que le câble pour
ce type de réseau est du câble blindé par paire, et qu'il faut utiliser les deux
fils d'une paire pour les connecteurs 1 et 2, une autre paire pour les
connecteurs 7 et 8, une troisième paire pour les connecteur 3 et 6 et enfin
la quatrième paire pour les connecteur 4 et 5. Si on respecte pas ce
schéma, le câble réseau se parasite lui même !

Pour résoudre ces différents types de problèmes, il faut réussir à isoler la cause…
Pour cela, testez les câbles, vérifiez les schémas de câblages utilisés, les endroits
où passent les câbles, les paramétrages des cartes.

Vous pouvez aussi essayer d'isoler la source en séparant une à une chaque
machine du réseau…

Bien sûr cette méthode risque d'être longue puisqu'il faut à chaque fois attendre un
certain temps (plusieurs jours souvent) pour vérifier si de nouveaux problèmes
surviennent, mais c'est souvent la seule solution

A part bien sûr installer une bonne fois pour toute un réseau de qualité en
changeant toutes les cartes, les câblages et les hubs… Il s'agit d'ailleurs souvent
d'une évolution logique chez les clients…

On va par exemple remplacer un vieux réseau 10 Mbits en coaxial poste à poste


par un réseau 100 Mbits en RJ 45 en étoile autour d'un hub, et comme par magie,
les problèmes disparaissent… Ne riez pas, c'est du vécu ☺ !

o Les problèmes logiques peuvent eux aussi être très variables. Citons les plus
courants :

Pas de serveur sur le réseau… On se trouve malheureusement encore


parfois confrontés à des installations sur des sites équipés en réseau poste
à poste en Windows 9x (voir 3.1x). Rien ne permet d'améliorer vraiment les
choses sur ce type de réseau, car ce sont les modules logiques de gestion
réseau propres à ces versions de Windows qui sont en cause…

Sachez que pour obtenir un bon niveau de fiabilité, il faut mettre en place
un serveur de fichier sous Windows NT, Novell, TwinServer ou Linux, les
solutions ne manquent pas…

Couches réseaux surabondantes : Très souvent, quand on examine la


configuration réseau des postes (et du serveur), on se rend compte que
tous les protocoles disponibles sur la machine sont installés (TCP/IP,
Netbeui, IPX/SPX, etc…).

Or, sauf cas exceptionnel, un seul est nécessaire pour que tout fonctionne.

Par expérience, le plus simple est de ne mettre en place que le protocole


TCP/IP, en attribuant une adresse IP fixe à chaque poste du réseau, de
manière à pouvoir se passer aussi du service d'attribution dynamique
d'adresse IP.

Au passage, quand vous choisissez les adresses IP pour vos postes,


choisissez les bien dans les plages d'adresses réservées pour les réseaux
locaux, ça vous évitera des problèmes lors de la connexion de votre réseau
à Internet :

• La classe A numéro 10 : 10.0.0.0 - 10.255.255.255


• 16 classes B entre 172.16 et 172.31. : 172.16.0.0 - 172.31.255.255 :
• 256 classes C commençant par 192.168. : 192.168.0.0 -
192.168.255.255

Encore un mot au niveau d'Internet : NetBeui installé avec un accès


Internet, ça s'appelle tout simplement une faille de sécurité !

Le serveur NT mal configuré : Très souvent, le serveur ne suit pas les


recommandations faites par Microsoft pour la configuration des couches
réseau ! Nous n'allons pas réécrire ici ce qui l'est déjà, par contre, nous
vous avons mis sur le CD/ROM une copie de la compilation de toutes ces
informations fournie par Philippe FEON et déjà disponible sur le site de
WinDevAsso.
Pour vous éviter un téléchargement de plus, vous le trouverez sur le
CD/Rom à l'emplacement : OUTILS\Reseau.zip

Linux +Samba : Nous ne nous étendrons pas sur ce sujet car, à ce jour,
nous ne prétendons pas avoir la compétence suffisante pour prôner
systématiquement le remplacement du serveur sous WinDows NT par un
serveur Linux avec Samba pour émuler le serveur de fichier de Windows.
Tout ce que nous pouvons vous dire, c'est que les expériences relatées par
ceux qui ont sauté le pas indiquent que :

• Il y a beaucoup moins (voire plus du tout) d'incidents réseaux

• A matériel égal, le serveur Linux+Samba est nettement plus rapide


que Windows NT

• Linux et Samba sont gratuits, donc, pour le prix d'un Windows NT


serveur + licences / poste, vous pouvez peut être payer un pro de
Linux pour l'installation et votre formation ☺.

Comme vous avez pu le constater, les causes des erreurs 7, 8 et 11 sont nombreuses et toutes dues
au matériel ou au système…

Si des détracteurs de WinDev prétendent que les problèmes de configuration de réseau sont
imaginaires car uniquement perceptibles avec WinDev, vous pouvez leur répondre, au choix :

• C'est faux, d'autres logiciels de gestion ont les même problèmes avec les mêmes causes
(vous trouverez un certain nombre de références à des logiciels très répandus du commerce
dans les archives de la ML WinDev Forum)

• Si c'était le cas, on se demande pourquoi Microsoft aurait mis ces informations à disposition
sur son site, dans les articles cités dans la compilation d'informations faite par Philippe Feon.

• Si jamais on vous a opposé le fait que travailler avec Word ou une autre application ne pose
pas de problème, répondez leur de comparer ce qui est comparable : travailler seul sur un
document texte, même s'il est situé sur un autre poste n'a techniquement RIEN de
comparable à travailler à plusieurs sur les enregistrements d'un même fichier.

De plus, un document Word reste rarement ouvert toute la journée, contrairement aux fichiers
d'une application de gestion. Enfin, le jour où le problème se produira sur un document
bureautique, le résultat sera très simple, il n'y aura plus de document bureautique du tout
(êtes-vous sûr que vous n'avez jamais eu un document Word ou Excel carrément disparu
après une coupure de courant ou un plantage de Windows ?).

16: LE FICHIER NE CORRESPOND PAS A SA DESCRIPTION

Cette erreur s'affiche lorsque la version du fichier de données est plus récent que la version de
l'analyse du programme.
Avec WDOutil (option "informations sur le fichier"), il faut vérifier que le numéro de génération du
fichier mémorisé dans le fichier de données et celui mémorisé dans le WDD sont les mêmes.

Si le numéro de génération du fichier de données est plus récent que le numéro de génération du
WDD, les causes sont les suivantes:

- le fichier n'a pas été mis à jour car les disquettes de mises à jour n'ont pas été lancées sur le
poste
Solution: lancer les disquettes d'installation clients
- la bibliothèque (qui contient le WDD), si elle existe, n'a pas été régénérée,
Solution: régénérer la bibliothèque
- le fichier de données ne se trouve pas dans le répertoire attendu (celui défini dans la description
du fichier ou celui indiqué par HSubstRep). Le chemin d'accès au fichier attendu est mémorisé
dans le .REP.

Solution: modifier le .REP s'il ne correspond pas ou installer les fichiers dans le bon répertoire.
Attention: il ne faut jamais déplacer "manuellement" un fichier de données: si un fichier est décrit
dans un répertoire et qu'il doit être installé dans un autre répertoire, il faut utiliser la fonction
HSubstRep.
- en programmation en langage externe, le programme n'a pas été recompilé avec les derniers
.WDI et WDR générés.
Solution: recompiler le programme avec les WDI et WDR adéquats

17: LE FICHIER NE CORRESPOND PAS A SA DESCRIPTION

Cette erreur s'affiche lorsque la version du fichier de données est plus ancien que la version de
l'analyse du programme.

Avec WDOutil (option "informations sur le fichier"), il faut vérifier que le numéro de génération du
fichier mémorisé dans le fichier de données et celui mémorisé dans le WDD sont les mêmes.

Si le numéro de génération du fichier de données est plus ancien que le numéro de génération du
WDD, les causes sont les suivantes:

- le fichier n'a pas été mis à jour car les disquettes de mises à jour n'ont pas été lancées sur le
poste
Solution: lancer les disquettes d'installation clients
- le fichier de données ne se trouve pas dans le répertoire attendu (celui défini dans la description
du fichier ou celui indiqué par HSubstRep). Le chemin d'accès au fichier attendu est mémorisé
dans le .REP.
Solution: modifier le .REP s'il ne correspond pas ou installer les fichiers dans le bon
répertoire.
Attention: il ne faut jamais déplacer "manuellement" un fichier de données: si un fichier est décrit
dans un répertoire et qu'il doit être installé dans un autre répertoire, il faut utiliser la fonction
HSubstRep.

- en programmation en langage externe, le programme n'a pas été recompilé avec les derniers
.WDI et WDR générés.
Solution: recompiler le programme avec les WDI et WDR adéquats

50: LE FICHIER NE CORRESPOND PAS À SA DESCRIPTION (FICHIER AU FORMAT


D'‫ة‬CHANGE).
La taille de l'enregistrement indiquée dans l'en-tête du fichier est différente de celle indiquée dans
le .WDD
Reportez-vous aux consignes indiquées en cas d'erreur 16 ou 17.
Les erreurs 16, 17 et 50 sont très différentes des précédentes. Ces trois erreurs résultent en fait
d'un outil formidable inclus dans WinDev, la modification automatique des fichiers de
données.

En effet, si WinDev vous permet de modifier à volonté la description des fichiers dans vos
analyses, il ne se contente pas de cela ! Au passage, il modifie la structure des fichiers de
données présents sur votre poste de développement, mais vous permet aussi d'effectuer les
mêmes modifications sur les postes de vos clients simplement en cochant une option dans le
module d'installation.

Prenons un exemple : La première version de votre analyse comprend une zone adresse de 20
caractères. Vous vous rendez ensuite compte que cette zone est trop courte et vous augmentez
sa taille à 40 caractères. Si vous livrez une nouvelle version de votre programme à un client qui
utilisait l'ancienne, le format de ses fichiers ne correspond plus au format nécessaire pour la
nouvelle version de votre application !

Si vous avez oublié de demander la modification automatique des fichiers de données dans votre
module d'installation, lorsque votre client lancera son nouveau programme, il se trouvera
confronté à l'une de ces erreurs.

La cause peut donc être un simple oubli de votre part mais il peut y en avoir d'autres.

Commencez donc par consulter l'aide en ligne de WinDev au chapitre "Modification automatique
des fichiers de données". Tout le mécanisme y est expliqué en détail.

Voyons maintenant les points importants… Si vous obtenez une erreur 16, 17 ou 50, vous savez
qu'il y a une incohérence entre le format des fichiers et leur description dans l'analyse. Si vous
avez bien demandé la modification automatique des fichiers, c'est que quelque chose s'est mal
passé.

La plupart du temps, le problème provient de la localisation des fichiers de données au moment


de la tentative de modification.

En effet, l'utilitaire qui réalise ce traitement (wdModFic) se base sur le contenu du fichier ".REP"
pour trouver les fichiers…
Si ce contenu n'est pas à jour, si vous utilisez des fichiers qui ne sont pas listés dans le .REP ou
qui ne sont pas accessibles au moment du traitement (sur des supports amovibles, sur des
disques réseaux non "montés", etc.…), le traitement ne peut être réalisé. Pour résoudre tous ces
problèmes, nous vous invitons à consulter en détail le chapitre consacré aux fichiers REP.

Deux autres cas peuvent encore se produire :

• Vous avez dû récupérer une sauvegarde d'un fichier de données et le fichier récupéré
utilise un format plus ancien…
• Vous avez récupéré une sauvegarde du programme ou réinstallé une version en vous
servant d'un CD autre que le dernier, et vous vous retrouvez avec une version de fichiers
de données plus récente que celle de votre programme.

Dans les deux cas, une solution très simple : installez la dernière version de votre programme en
demandant la modification automatique des fichiers de données et tout rentrera dans l'ordre.

Maintenant que nous avons examiné les cas d'erreur les plus récurrents, voyons comment y faire
face.

La réparation

Nous n'aborderons pas ici le cas des erreurs 16, 17 et 50 dont les solutions sont largement traitées
dans le chapitre sur les fichiers ".REP".

Par contre les erreurs 7, 8 et 11 méritent largement de prendre le temps de chercher des solutions.

La première solution à essayer est de réindexer les fichiers… Si l'erreur persiste, le plus simple est
bien évidemment de récupérer une sauvegarde du fichier abîmé.

Vous ne pouvez pas récupérer de sauvegarde , Il n'y en pas, il y a eu trop de travail effectué depuis
la dernière ? C'est le moment pour vous de mettre en place un système de sauvegarde automatique
(vous trouverez un logiciel gratuit, AxlSauve, qui fait cela très bien sur le CD/ROM à l'emplacement :

\OUTILS\AXLSAUVE.Zip) !

Mais comme il est trop tard pour s'en sortir avec une sauvegarde, il faut réparer le fichier…

Il faut tout d'abord séparer les différents types de problèmes que vous allez peut être devoir réparer :

1. Le fichier de données est abîmé, il est impossible de le réindexer :Vous avez non seulement
des erreurs pendant les accès normaux, mais en plus vous en avez pendant la réindexation.

2. Le fichier de données est abîmé, il est impossible d'afficher certains enregistrements :


Lorsque vous affichez un enregistrement à l'écran, un message d'erreur indique que le
contenu d'une zone ne correspond pas à ce qu'il devrait être…

3. Le fichier de données est abîmé, la numérotation de l'identifiant automatique ne fonctionne


plus : lorsque vous ajoutez un enregistrement dans le fichier, une erreur de doublon survient
alors que la seule clé unique est l'identifiant automatique ou que toutes les autres clés
uniques sont correctes.

4. Le fichier mémo est abîmé : La réindexation se passe normalement, mais quand vous essayer
d'afficher un ou des enregistrements, vous avez quand même une erreur. Si vous débranchez
temporairement la gestion des doublons, vous n'avez plus d'erreur.

Vous pouvez exporter le projet nommé GDWProbHF à partir de l'interface du Guide.

Ce projet regroupe différentes fenêtres de réparation pour gérer les différents cas ci-dessus.

Incroyable ? Nous vous avons mâché le travail ☺ ! Il est basé sur des fichiers standard de WinDev à
peine modifiés et contient toutes les fenêtres RAD de gestion de fichier pour vous permettre de
créer/parcourir/modifier des fiches.

La fenêtre de menu à été modifiée pour permettre d'appeler 4 fenêtres de traitement correspondant à
:

• la réindexation des fichiers (c'est la fenêtre standard générée par WinDev)

• le test et la correction des enregistrements (.FIC)

• le test et la correction des mémos (.MMO)

• la correction de l'identifiant automatique

Ces 4 fenêtres sont intégrables telles quelles dans vos applications, mais vous gagnerez sûrement à
les modifier pour adapter leur traitement (qui se veut générique) à vos cas particuliers.

Mais examinons d'abord les différentes stratégies à employer :

Dans les cas 1 et 2, il faut réussir à déterminer quels sont les enregistrements en erreur et choisir
ensuite entre les supprimer ou en écraser le contenu. Nous préférons cette dernière solution car les
enregistrements peuvent être reliés à d'autre fichiers et supprimer en aveugle ces enregistrements
peut déclencher des erreurs en cascade.

Pour cela, il faut parcourir tout le fichier et afficher le contenu de chaque enregistrement. Il faut de
plus intercepter les erreurs de manière à pouvoir déterminer quels enregistrements sont abîmés et
doivent être remis à zéro ou à blanc.

Dans le cas numéro 4, le problème est le même, à ceci prés que ce sont les zones mémos qui sont
concernées au lieu des zones de l'enregistrement principal. Comme le contenu des zones mémos
peut être très divers, il n'est pas possible de les afficher/tester dans un traitement générique.

Dans notre exemple, nous avons donc choisi de séparer les cas 1 et 2 d'une part et 4 d'autre part…
Ne sachant pas au départ si les enregistrements seuls sont abîmés et/ou si les mémos le sont, il faut
commencer par effectuer le traitement sur les enregistrements en débranchant la gestion des mémos,
puis, une fois que l'on est certain que les enregistrements sont techniquement valides, on peut alors
lancer le parcours du fichier avec la gestion des mémos branchés.

En essayant de tout faire dans le même traitement on ne saurait pas si le problème provient d'une
enregistrement ou d'un mémo, et on remettrait donc à zéro des zones qui n'en ont pas besoin. Nous
avons donc choisi une méthode permettant de minimiser la perte d'information.

Le cas numéro 3 est très différent… Il faut comprendre comment les identifiants automatiques sont
gérés : Dans l'enregistrement numéro 0 de chaque fichier HF, les octets 0F à 12 contiennent le plus
grand numéro d'identifiant automatique attribué pour ce fichier.

Si une erreur système a empêché l'écriture de cette information ou provoqué l'écriture d'une
information erronée, chaque fois que le moteur hyperfile essaye d'ajouter un enregistrement, vous
vous retrouverez avec une erreur de doublon sur clé unique.

Pour résoudre ce problème, il faut remettre la bonne valeur dans l'entête du fichier. Vous pouvez bien
sur le faire à la main, avec un éditeur hexadécimal, mais nous vous avons concocté un petit utilitaire
qui le fait pour vous.

Dans le projet d'exemple, une fenêtre se charge de parcourir le fichier de votre choix, sans utiliser de
clé (nous sommes dans le cas où le fichier a des problème et nous ne sommes donc pas sûr à ce
moment de la validité des informations d'index). La plus grande valeur de l'identifiant automatique est
conservée puis écrite dans l'entête du fichier à la fin du traitement.

Bien sûr, vous pouvez vous servir de cet exemple pour modifier le contenu de cette zone si vous en
avez envie, mais nous vous le DECONSEILLONS FORMELLEMENT ! Cette zone ne devrait être
modifiée que EN CAS DE PROBLEMES GRAVES sur le fichier !

Lorsque le traitement implique l'affichage des informations contenues dans les zones du fichier,
il est nécessaire que la fenêtre XX_FIC générée par le RAD et comportant TOUS les champs du
fichier soit disponible, même si vous ne l'utilisez pas habituellement dans votre programme. Ceci est
nécessaire pour rendre le traitement générique. Comme vous disposez des sources du projet, rien ne
vous empêchera de modifier ce fonctionnement par défaut pour l'adapter à votre cas, et même
l'affiner.

Vous pouvez envisager beaucoup d'améliorations dans ces traitements et nous allons vous
proposer un certain nombre d'idées ci-après. Nous avons choisi de ne pas les implémenter dans
notre exemple pour plusieurs raisons :

• Certaines de ces idées nécessitent des adaptations spécifiques à une application

• L'implémentation de certaines d'entre elles dépend d'un choix du mode de réparation que
vous seul ou vos clients peuvent faire

• Nous n'allons quand même pas faire tout le travail à votre place ☺ !

Mais voyons plutôt ces idées :

• Au lieu de faire un hRaz de l'enregistrement complet dans le traitement des fichiers FIC, on
pourrait envisager de faire une boucle sur les champs de chaque enregistrement et de les
afficher l'un après l'autre… En faisant ainsi, on pourrait déterminer précisément LE champ qui
est en erreur et ne vider que CELUI-CI.

• La réparation des fichiers mémos pourrait passer par une phase d'affichage/contrôle
spécifique au contenu de chaque champ pour améliorer le contrôle et ne pas vider TOUS les
champs mémos d'un fichier si un seul est en erreur

• Il serait possible de faire un traitement qui traite/vérifie complètement un ou tous les fichiers
en combinant ces différents traitements :
o Traitement du fichier FIC,
o puis du fichier MMO,
o au passage vérification/correction de l'identifiant automatique
o et finalement réindexation avec compactage

Et nous sommes certain que vous en trouverez beaucoup d'autres…

Voilà, vous connaissez maintenant les causes de la plus grande partie des problèmes rencontrés sur
des fichiers hyperfile et comment les réparer, vous vous sentirez sûrement beaucoup mieux quand un
client vous appellera au secours…

Mais rassurez vous quand même ! Ce n'est pas parce qu'un chapitre entier est consacré à ces
problème qu'ils surviennent souvent… Notre plus gros problème pour mettre au point ces modules de
réparation a été de réussir à casser suffisamment les fichiers hyperfile pour pouvoir faire des tests
valables ☺.

C'est pour cette raison que nous ne pouvons en aucun cas vous garantir que ces modules
seront toujours capables de réparer vos fichiers. Ils nous ont permis de le faire dans les cas que nous
avons pu tester, mais vous les utiliserez sous votre seul et entière responsabilité. Et n'oubliez pas de
faire une (ou plusieurs) copies de sauvegardes de vos fichiers avant de les traiter ☺.

Si vous trouvez des cas de problèmes sur les fichiers HF qui ne sont pas résolus par ces modules,
des améliorations à ces programmes ou des idées intéressantes à ce sujet, n'hésitez pas à en faire
profiter la communauté des développeurs WinDev.

oOoOooOoOoOoOo
Importation et Exportation Ascii
Dans ce chapitre, nous allons vous fournir des solutions simples d'importation et d'exportation de
données, car nous savons que ces solutions sont très souvent nécessaires dans la plupart des
applications développées avec WinDev…

De plus en plus, les applications doivent être capables de communiquer entre elles et d'utiliser des
données provenant de sources externes, comme par exemple, fournir des données à d'autres
logiciels.

De nombreuses possibilités s'offrent à vous quand vous abordez ce chapitre de la programmation


WinDev… En effet, WinDev fournit différents mécanismes sophistiqués permettant l'échange
d'informations avec d'autres applications comme les liens DDE et la gestion de L'OLE…

Certains chapitres de cet ouvrage traitant en profondeur de ces techniques, nous nous contenterons
de traiter ici de l'importation et de l'exportation de données au format Ascii qui reste, malgré son
grand âge, une des méthodes les plus utiles pour partager des informations.

Puisque aujourd'hui pratiquement toutes les applications disposent de fonctions d'importation et


d'exportation ascii, vous devez sérieusement envisager d'en incorporer dans vos propres
développements.

Mais pas d'inquiétude… Si nous allons effectivement décortiquer un peu les tenants et les
aboutissants de ces traitements, nous vous fournissons aussi en annexe un projet d'exemple
GDWImpEx (que vous pouvez installer sur votre machine depuis l'interface du Guide…) comportant
en particulier deux fenêtres très intéressantes, l'une pour l'importation, l'autre pour l'exportation.

Si elles sont très largement perfectibles, elles représentent malgré tout une solution d'import/export
basique directement incorporable à vos applications, sans aucune modification.. Mais vous en saurez
plus à ce sujet à la fin de ce chapitre.

Formats des fichiers ascii

Examinons tout d'abord la structure des fichiers ascii les plus courants. Un fichier ascii étant un
simple fichier texte, il est clair que son contenu peut prendre n'importe quel format (et certains
éditeurs ou administrations n'hésitent pas à compliquer ce format à plaisir ☺).
Toutefois, dans la grande majorité des cas, on rencontre deux types de fichiers :

• Les fichiers en "longueur fixe". Dans ce cas, chaque enregistrement du fichier à la même
taille, et une zone de l'enregistrement est caractérisée par une position et une longueur…
• Les enregistrements sont parfois séparés les uns des autres par un caractère de fin
d'enregistrement, mais cela n'est pas obligatoire, puisque leur taille fixe permet de les repérer
précisément.

Un fichier de ce type ressemble donc à quelque chose comme cela :

DUGLAS ALAIN DIACOM INTERNATIONAL diacom@aol.com


HARARI FABRICE AXLINFO fharari@axlinfo.com

• Les fichiers "délimités". Dans ce cas, les enregistrements peuvent avoir des tailles variables et
sont en général séparés les uns des autres par un retour chariot. Chaque zone est séparée
des autres par un caractère "délimiteur" (; TAB ou autre).
• De plus, le contenu des zones est souvent encadré par des guillemets ("). Dans ce cas, le
fichier ressemblerait plutôt à ceci :

"DUGLAS";"ALAIN";"DIACOM INTERNATIONAL";"diacom@aol.com"
"HARARI";"FABRICE";"AXLINFO";"fharari@axlinfo.com"

Bien sûr, de nombreuses variantes peuvent exister (en particulier pour le type délimité) et vous
pouvez être confronté à :

• Des séparateurs très variés (: ; , TAB ou autre),

• La présence ou l'absence des guillemets, parfois remplacés par d'autres caractères,

• La présence d'une ligne d'en-tête (header) comportant les noms des différentes zones…

Sachez tout de même que l'ascii délimité est le format que l'on rencontre le plus souvent…

Si vous êtes amené à exporter des données sans savoir quel logiciel va les lire, vous pouvez
utiliser un "truc" pour rendre votre fichier plus "universel" !

Combinez les deux méthodes ! Utilisez des longueurs fixes pour toutes les zones, mais séparez les
quand même par des séparateurs… Vous obtiendrez alors quelque chose qui ressemblera à ceci :

"DUGLAS ";"ALAIN ";"DIACOM INTERNATIONAL ";"diacom@aol.com


"HARARI ";"FABRICE";"AXLINFO ";"fharari@axlinfo.com

Ce format mixte pourra alors être lu par n'importe quel programme implémentant l'importation de l'un
ou de l'autre de ces deux formats…

En longueur fixe, on indiquera que la zone nom commence au caractère 2 (pour éviter le guillemet) et
à une longueur de 7 caractères et que la zone suivante ne commence qu'au caractère 12, évitant
ainsi les guillemets et le séparateur.

En délimité, il y aura simplement des espaces en fin des différentes zones, ce qui ne devrait pas
poser de problème.

Lecture d'un fichier ascii

Pour WinDev, un fichier ascii est un fichier externe tout ce qu'il y a de classique. WinDev fournit
d'ailleurs deux méthodes pour lire dans ces fichiers, les instructions fLit et fLitLigne.

Nous ne nous étendrons pas sur le sujet, l'aide en ligne de ces instructions et les exemples fournis
dans le guide étant largement suffisants pour l'apprentissage de leur utilisation…

Examinons-les plutôt par rapport à nos différents besoins : la lecture d'un fichier délimité et la lecture
d'un fichier en longueur fixe…

FLitLigne vous permet de lire le contenu d'une ligne d'un fichier externe, la définition d'une ligne
étant une zone de texte terminée par un RC+LF (retour chariot plus Line Feed, soit les caractères
ascii 13 et 10).

Cette instruction est donc parfaitement adaptée au traitement des fichiers ascii délimités qui sont
justement structurés de cette manière.

Par contre, les fichiers en longueur fixe ne comprennent pas toujours des RC+LF entre les
enregistrements. Dans ce cas, il convient donc d'utiliser l'instruction fLit qui permet quant à elle de
définir précisément le nombre de caractères que l'on souhaite lire.

Le traitement à effectuer pour l'importation d'un fichier ascii peut se résumer ainsi :

• Ouverture du fichier ascii en mode lecture (instruction fouvre),

• Boucle en lecture des différents enregistrements (instructions flit ou flitligne selon le cas),

o Traitement de l'enregistrement, à savoir :

Contrôles éventuels,

Affectation des informations dans les zones du ficher HyperFile,

Ajout de l'enregistrement dans le fichier HyperFile,

• Quand le fichier ascii a été traité en totalité, fermeture de celui-ci (instruction fferme).

On parle ici d'ajout d'enregistrement dans le fichier HyperFile, car c'est le cas le plus courant,
mais dans certains cas, vous pouvez être amenés à utiliser ce système pour modifier des
enregistrements existants, voire en supprimer, en fonction du contenu d'une des zones du fichier
ascii.

Un système de ce genre peut donc permettre de gérer la réplication d'informations entre différentes
bases de données de manière très souple.

Il convient aussi d'examiner l'instruction hImporteTexte qui a la prétention de pouvoir remplacer


tout le traitement ci-dessus par une seule ligne de code. En théorie, c'est fantastique, mais en
pratique, vous ne pourrez l'utilisez que dans un nombre de cas très restreints.

En effet, elle ne traite que des fichiers délimités, et gère un certain nombre de cas bien précis, sans
aucune possibilité d'intervention de votre part.

Si un problème non prévu survient lors de l'importation d'un fichier avec cette méthode, vous
n'obtiendrez qu'un compte rendu d'erreur.

Avec un traitement "manuel", vous pourrez déterminer précisément quoi faire dans ce cas (ignorer la
ligne et continuer, demander l'avis de l'utilisateur, etc, etc)

Enfin, bien entendu, cette instruction ne fonctionne que pour l'importation dans un fichier HyperFile,
alors que le traitement "manuel" vous permet d'importer tout aussi bien dans un fichier xBase, une
base Access, une base SQL, voire tout simplement dans une table mémoire si l'utilisateur veut
pouvoir vérifier / modifier les données avant l'importation…

Ecriture dans un fichier ascii

Pour l'exportation de données dans un fichier ascii, le schéma est exactement "parallèle" :

• Ouverture du fichier ascii en mode écriture (instruction fouvre),

• Boucle en lecture des différents enregistrements du fichier HyperFile à exporter,

o Traitement de l'enregistrement, à savoir :

Contrôles éventuels,

Préparation d'une chaîne de caractères au bon format (délimité ou longueur


fixe),

Ecriture de cette chaîne dans le fichier ascii (instructions fecrit),

• Quand le fichier HyperFile a été traité en totalité (ou au moins tous les enregistrements à
exporter), fermeture de celui-ci (instruction fferme).

Vous voyez, rien de bien compliqué ! Bien sûr, vous trouverez un exemple complet d'exportation
GDWImpEx que vous pouvez exporter depuis l'interface du Guide
OEM <-> ANSI <-> Autres formats

Souvent lors de traitement d'importation ou d'exportation de données, on se trouve confrontés aux


limites des "standards".

En effet, on pourrait croire qu'à partir du moment où on parle de fichier ASCII, leur encodage est
toujours identique… Et bien ce n'est pas le cas ! Voyons donc ensemble les problèmes que vous
pouvez rencontrer :

Vous obtenez des caractères bizarres à la place des caractères accentués ? Si votre fichier provient
d'un PC, c'est certainement un problème d'encodage ANSI / OEM… S'il provient d'un ordinateur plus
"exotique" (MacIntosh par exemple), le problème sera peut être plus compliqué à résoudre.

Il existe en effet de nombreuses variantes de la norme ASCII. Rien que sur plate-formes PC, vous
pouvez vous trouver face à de l'ascii OEM (typiquement norme MS/DOS) ou à des caractères à la
norme ANSI (norme WINDOWS).

WinDev fournit deux instructions très utiles dans ce cas : OEMVersAnsi et AnsiVersOEM… Ces
deux instructions permettent de transcoder très facilement une chaîne d'un format à l'autre…

Par contre, si votre fichier est plus exotique, il vous faudra, soit obtenir une version plus classique
(sur MacIntosh par exemple, il existe des outils permettant de transformer du texte "Mac" en texte
PC), soit transcoder vous-même vos fichiers lors de l'importation.

Mais vous n'en avez pas encore fini avec les fichiers ascii…

En effet, vous pouvez avoir tout un tas de variantes pour les délimiteurs d'enregistrement.

Par exemple, certains fichiers provenant du monde Unix peuvent utilisent un simple LineFeed comme
délimiteur (caractère 10), voire un LF+CR (caractères 10 + 13)…

Enfin, méfiez vous !

Fichier texte ne veut pas forcément dire fichier Ascii ! Il existe d'autres "standards" d'encodage dans
le monde informatique, comme par exemple l'EBCDIC…Ce dernier est un standard provenant des
gros systèmes IBM et est totalement différent de la norme ascii…

Fort heureusement, il devient de plus en plus rare de se trouver confronté à des fichiers de ce type,
autrefois fort répandus.

Projet WinDev d'Import/Export

Maintenant que nous avons bien insisté sur tous les problèmes que vous risquez de rencontrer,
passons aux bonnes nouvelles : vous pouvez donc exporter, depuis l'interface du Guide, le projet
d'exemple GDWImpEx, permettant l'importation et l'exportation de fichiers ascii.
Examinons ensemble ses caractéristiques :

Sa base a été générée par le RAD à partir de fichiers d'exemple de WinDev… Ceci pour vous
permettre de faire des essais avec des fichiers que vous pourrez examiner ou modifier à volonté.

Vous pouvez réutiliser les fenêtres d'import et d'export (GDWImpor et GDWExpor) directement
dans n'importe laquelle de vos applications. En effet, les routines utilisées sont génériques et se
chargent de retrouver de manière dynamique les noms des fichiers et de leurs champs.

Ces exemples traitent uniquement des fichiers délimités d'un type particulier (; et "), mais il vous sera
facile de modifier ce code en fonction de vos besoins.

Les modules d'import et d'export vous permettent de choisir quelles rubriques traiter et dans quel
ordre.

Le module d'import vous permet par ailleurs de visualiser le début du fichier ascii à importer, de façon
à faciliter le travail de mise en correspondance des zones.

Le module d'import permet en outre d'ignorer certaines zones du fichier ascii, en ajoutant
l'information "Rubrique à ignorer" dans la liste, ce qui vous permet de n'importer que les zones qui
vous intéressent.

L'étude des différentes fonctions de ces exemples devraient vous permettre de bien comprendre ces
différents traitements et de les améliorer en fonction de vos besoins spécifiques…

Quelles améliorations ?

Par exemple :

• Ajout de tests pour sélectionner les enregistrements à exporter,

• Implémentation des traitements pour les fichiers en longueur fixe,

• Choix des délimiteurs à utiliser,

• Traitement d'exportation utilisant une des clés du fichier HyperFile au lieu de faire une boucle
sur hlit,

• Exportation complexe de fichiers liés,

• Importation d'un fichier ascii dans plusieurs fichiers HyperFile reliés,

• Traitements d'importation différents en fonction d'une des zones du fichier à importer (ajout,
modification, suppression), ce qui, lié à une fonction d'exportation de tous les enregistrements
modifiés / supprimés / ajoutés dans une base, conduit à une puissante fonction de réplication

Comme vous le voyez, les possibilités sont immenses, et nous sommes sûrs que vous trouverez
encore bien d'autres idées à ce propos…

oOoOoOoOoOoOo
Dates et Heures en WinDev

Les fonctions de gestion des dates et des heures en WLangage sont nombreuses et souvent
suffisantes pour la grande majorité des traitements courants.

Nous allons essayer ici de vous apporter des informations complémentaires et des routines utiles
dans les problèmes de gestion courante en vous offrant des fonctions complémentaires.

Tout d'abord, sachez que WinDev stocke les dates et les heures sous forme de chaînes.

Une date comme le 27/12/2000 est stockée dans une chaîne de 8 caractères sous la forme
20001227.

WinDev permet également d'utiliser un format de date court avec l'année stockée sur 2 chiffres, mais
maintenant que nous avons passé le cap du bug de l'an 2000, plus personne ne devrait l'utiliser.

De la même façon, une heure comme 16h15m25s12c est stockée dans une chaîne au format
16152512.

Ces deux formats permettent un tri facile des informations que ce soit dans une table ou dans une clé
de fichier.

Lors de l'utilisation de champs de saisie de type date et heure, deux formats sont gérés : le format de
stockage interne et le format de saisie / affichage.

Ainsi, les utilisateurs saisiront la date au format JJ/MM/AAAA (ou MM/JJ/AAAA si nécessaire), mais
quel que soit le format de saisie utilisé, elle sera stockée sous la forme AAAAMMJJ. Ceci est vrai
également pour les heures.

Les fonctions DateVersChaine et ChaineVersDate sont là pour vous permettre de passer d'un de
ces formats à l'autre quand vous devez gérer des dates à un autre niveau que des champs de saisie
(extraction depuis un fichier texte par exemple).

Dans les fichiers HyperFile, lorsque vous déclarez un champ comme étant de type date, il s'agit donc
d'une chaîne de 8 octets.

Par contre, soyez méfiants à cet égard : si vous déclarez un champ comme étant de type heure,
seuls 4 octets sont utilisés et vous ne pourrez donc stocker que les heures et les minutes. Si vous
avez besoin de stocker aussi les secondes et/ou les centièmes de secondes, il vous faudra le faire
dans un simple champ chaîne de longueur 6 ou 8.

Lorsque vous avez besoin de faire des calculs sur des dates / heures, vous disposez des fonctions
DateVersEntier et HeureVersEntier qui vous permettent, comme leur nom l'indique, de transformer
vos chaînes en entiers, chaînes que vous pouvez ensuite additionner ou soustraire…
Le résultat est un entier long, mais vous devez vous méfier de ces fonctions dans plusieurs cas :
• DateVersEntier ne fonctionne que pour une date supérieure au 1er janvier 1800 (qui
correspond à la valeur 1)
• HeureVersEntier ne fonctionne que pour une heure comprise entre 00h00 et 23h59m59s99c
• Ces deux fonctions affichent un message dans le cas où on leur fournit une date ou une heure
invalide. Si vous les utilisez dans une formule de calcul, ce n'est sûrement pas ce que vous
souhaitez...

Que peut-on faire pour résoudre ces problèmes ?

Pour les deux premiers, il faudra faire vos calculs vous-même en contournant le problème…

Par exemple, si vous devez additionner des heures mais que le résultat peut dépasser 24 heures :

TrEntier est un entier long // qui contient le résultat de vos additions


TrNbJour est un entier=0 //
// Le code ci-dessous incrémente un compteur de jours tantque TrEntier ne
// contient pas une valeur correspondant à un nombre d'heures inférieur à 24.
Tantque TrEntier>=8640000 alors
TrNbJour++
TrEntier=TrEntier-8640000
Fin

En ce qui concerne les messages affichés, imaginons que vous deviez placer des formules de calcul
dans votre code et que ces formules travaillent sur des zones saisies par l'utilisateur. Ce dernier peut
saisir une date ou laisser la zone vide… Si votre code contient la ligne :

TrEntier=TrEntier+DateVersEntier(DateSaisie)

et que l'utilisateur a laissé la zone "DateSaisie" vide, vous obtiendrez un superbe message d'erreur
pendant l'exécution.

Vous pouvez bien sûr tester chaque date et chaque heure avant de l'utiliser, mais si vous avez de
nombreuses zones, votre code ressemblera vite à un plat de spaghettis.

Nous vous proposons une formule un peu plus "élégante". Il suffit d'encapsuler la fonction
DateVersEntier dans une procédure globale que nous appellerons GDWDateVersEntier :

Procédure GDWDateVersEntier(DateChaine)
//Teste si la date est correcte
//Renvoie un entier si c'est le cas, 0 Sinon
Si DateValide(DateChaine) alors
renvoyer (dateversentier(DateChaine))
Sinon
renvoyer 0
Fin
Ainsi que vous le constatez, cette procédure se charge de faire le test de validité et renvoie 0 dans le
cas où la date n'est pas valide.

Dans une formule de calcul, si l'utilisateur a laissé la zone vide, cela veut dire qu'il ne faut rien
additionner et écrire :

TrEntier=TrEntier+GDWDateVersEntier(DateSaisie)

est donc maintenant suffisant.

De la même façon, la procédure GDWHeureVersEntier renverra 0 ou la bonne valeur en fonction de


la validité du paramètre.

Procédure GDWHeureVersEntier(HeureChaine)
//Teste si l'heure est correcte
//Renvoie un entier si c'est le cas, 0 Sinon
Si HeureValide(HeureChaine) alors
renvoyer (HeureVersEntier(HeureChaine))
Sinon
renvoyer 0
Fin

La suite de ce chapitre se propose de vous fournir "clé en mains" un certain nombre de fonctions qui
travaillent sur les dates et les heures… Elles vous épargneront sûrement quelques heures de travail.

Les procédures AjouteJour, AjouteMois et AjouteAnnee vous permettent en une seule instruction
d'ajouter à une date quelconque un nombre quelconque de Jours, de Mois ou d'Années ; ce nombre
pouvant bien sûr être négatif.

En retour, elles renvoient la date correspondant au résultat trouvé. Si la date initiale n'est pas valide,
ces fonctions la renvoient telle quelle, sans faire aucune addition.

AjouteJour :

Procédure AjouteJour(TrDate,NbreJours)
// Ajoute le nbre de jours (positif ou négatif) à la date passée en paramètre et
// renvoie la date correspondante
// Si la date n'est pas valide, la renvoie telle quelle
Si pas datevalide(TrDate) alors renvoyer TrDate
Renvoyer entierversdate(dateversentier(TrDate)+NbreJours)

AjouteMois :

Procédure AjouteMois(TrDate,NbreMois)
// Ajoute le nbre de mois (positif ou négatif) à la date passée en paramètre et
// renvoie la date correspondante
// Si la date initiale est incorrecte, la renvoit telle quelle
Si pas datevalide(TrDate) alors Renvoyer TrDate

TrChaine est une chaine


TrEntier est un entier
TrAnnee est un entier
TrAnnee=gauche(TrDate,4)
TrEntier=val(Milieu(TrDate,5,2))+NbreMois
tantque TrEntier>12
TrEntier=TrEntier-12
TrAnnee=TrAnnee+1
Fin
tantque TrEntier<0
TrEntier=TrEntier+12
TrAnnee=TrAnnee-1
Fin
TrChaine=NumériqueVersChaine(TrAnnee,"04d")+…
NumériqueVersChaine(TrEntier,"02d")+droite(TrDate,2)
//Cas des mois de 31 jours <-> 30 ou 29 ou 28
Tantque pas dateValide(TrChaine)
TrEntier=val(Droite(TrChaine,2))-1
TrChaine=Gauche(TrChaine,6)+NumériqueVersChaine(TrEntier,"02d")
Fin

Renvoyer TrChaine

AjouteAnnée :

Procédure AjouteAnnee(TrDate,NbreAnnee)
// Ajoute le nbre d'années (positif ou négatif) à la date passée en paramètre et
// renvoie la date correspondante
// Si la date est incorrecte au départ, la renvoit telle quelle
Si pas datevalide(TrDate) alors renvoyer TrDate

TrChaine est une chaine


TrEntier est un entier
TrEntier=val(gauche(TrDate,4))+NbreAnnee
TrChaine=NumériqueVersChaine(TrEntier,"04d")+droite(TrDate,4)

// Cas du 29 Février
Tantque pas dateValide(TrChaine)
TrEntier=val(Droite(TrChaine,2))-1
TrChaine=Gauche(TrChaine,6)+NumériqueVersChaine(TrEntier,"02d")
FIN
renvoyer TrChaine

La procédure qui suit est nettement plus complexe...

Elle se propose en effet de calculer pour vous une date d'échéance en fonction d'un mode de
règlement plus ou moins tarabiscoté et d'une date initiale :
Procédure DateEcheance(DateInitiale,NbreJours,FinDeMois,JourEcheance)
// Retourne la date d'échéance en fonction de la
// date initiale et du mode de règlement. Ce mode de règlement est composé
// de 3 éléments :
// - Nombre de jours (30, 45, 60…)
// - D'un booléen signalant si l'échéance est en fin de mois
// - D'un jour d'échéance (échéance à 45 jours fin de mois le 10)
// Dans le cas d'un règlement en espèces ou en chèque à réception,
// on aura bien sur 0,0,0 comme mode de règlement, alors que dans le
// cas évoqué ci-dessus, on aura 45,1,10)
// On considère ici que la date initiale est valide (il s'agit en général
// d'une date de facture, donc, il vaut mieux qu'elle soit valide)

TrDate est une chaine


TrJour,TrMois,TrAnnee sont des entiers
TrDateEntier est un entier long
TrDateEntier=DateVersEntier(DateInitiale)

// Ajout du nombre de jours


TrDateEntier=TrDateEntier+NbreJours
TrDate=EntierVersDate(TrDateEntier)

// Eventuellement passage à la fin du mois


Si FinDeMois alors
TrJour=31
TrDate=gauche(TrDate,6)+NumeriqueVersChaine(TrJour)
//Boucle pour prise en compte des mois de 30, 29 ou 28 jours
Tantque pas DateValide(TrDate)
TrJour--
TrDate=gauche(TrDate,6)+NumeriqueVersChaine(TrJour)
Fin
Fin

// Eventuellement passage au jour d'échéance du mois suivant


Si JourEcheance>0 alors
//le jour d'échéance est il supérieur au jour courant (même mois)
TrJour=droite(TrDate,2)
TrMois=milieu(TrDate,5,2)
TrAnnee=gauche(TrDate,4)
Si JourEcheance>TrJour alors
TrJour=JourEcheance
Sinon //Passage au mois suivant en plus
TrJour=JourEcheance
Si TrMois<12 alors
TrMois++
Sinon
TrMois=1
TrAnnee++
Fin
Fin
//Et on reconstruit la date
TrDate=NumeriqueVersChaine(TrAnnee)+…
NumeriqueVersChaine(TrMois,"02d")+…
NumeriqueVersChaine(TrJour,"02d")
Fin

Renvoyer TrDate

Imaginons que vous vouliez imprimer la date d'échéance d'une facture dont le mode de règlement est
une traite à 45 jours fin de mois le 10, il suffira d'écrire :

IImprime("Date d'échéance : "+DateEcheance(DateFacture,45,1,10))

Difficile de faire plus simple ! ☺

Voyons maintenant comment gérer facilement la saisie d'heures dans des formats variés :

Votre logiciel va être utilisé de manière internationale, et vos utilisateurs ont des habitudes très
différentes à ce niveau : 14h12 pour l'un devient 02h12pm pour l'autre (ou 02h12p), 09h10 devient
09h10am ou 09h10a.

Comment gérer simplement la saisie des heures de rendez-vous quand on ne sait pas au départ quel
format l'utilisateur va vouloir employer ?

La réponse est le couple de procédures GDWChaineVersHeure et GDWHeureVersChaine !

Il vous suffit alors de mettre en place un champ de saisie de type chaîne sur 7 caractères, et dans le
code de sortie de celui-ci, de placer la ligne :

MoiMeme=GDWHeureVersChaine(GDWChaineVersHeure(MoiMeme))

GDWChaineVersHeure va transformer la saisie de l'utilisateur en chaîne horaire universelle


classique sur 24 heures (HHMM) et ce quel que soit le format de saisie employé…

De son coté, GDWHeureVersChaine va transformer cette chaîne horaire au format universel en une
chaîne horaire "locale" en se basant sur un paramètre de format.

Mais voyons d'abord comment transformer une saisie rapide et approximative en chaîne horaire
universelle :

Procedure GDWChaineVersheure(TrChaine)
// Cette fonction transforme TrChaine (qui est une chaîne
// contenant une heure qui peut être formatée de
// différentes manières : Temps universel sur 24h (HH:MM)
// ou format anglais (HH:MMam), ou encore format "approximatif"
// anglais (HHMMa ou HH:MMa) ou encore format "approximatif"
// universel (HHMM) ou encore un format incomplet (H ou HH…)
// On ne peut pas employer heureValide, cette fonction ne
// travaillant que sur des heures au format HH:MM, donc :
Si SansEspace(TrChaine)="" alors renvoyer ""

// On passe en minusucle pour que AM et am soient équivalents


TrChaine=minuscule(TrChaine)
TrHeure est une chaîne
TrHeures,TrMinutes sont des entiers
TrFin est un entier=0

// Saisie Anglaise ou Universelle ?


Si position(TrChaine,"a")>0 alors
TrFin=position(TrChaine,"a")
Sinon
Si position(TrChaine,"p")>0 alors
TrFin=position(TrChaine,"p")
Fin
Fin

// Extraction des heures et des minutes


Si TrFin>0 alors
// Nous supposons donc que l'heure a été saisie en mode anglais
// A-t-on saisi le séparateur ":" ?
Si position(TrChaine,":")>0 alors
TrHeures=gauche(TrChaine,Position(TrChaine,":")-1)
TrMinutes=Milieu(TrChaine,Position(TrChaine,":")+1,…
TrFin-(Position(TrChaine,":")+1))
Sinon //Pas de séparateur de saisi, on va essayer d'utiliser la taille
TrHeures=gauche(TrChaine,2)
TrMinutes=Milieu(TrChaine,3,2)
Fin
// Maintenant, on devrait avoir les valeurs dans Trheures et TrMinutes
// On doit les transformer en format universel
Selon Milieu(TrChaine,TrFin,1)
cas "a"
Si TrHeures=12 alors TrHeures=0
cas "p"
Si TrHeures<>12 alors TrHeures=TrHeures+12
Fin
Sinon // La saisie a été faite au format universel
// A-t-on saisi le séparateur ":" ?
Si position(TrChaine,":")>0 alors
TrHeures=gauche(TrChaine,Position(TrChaine,":")-1)
TrMinutes=Milieu(TrChaine,Position(TrChaine,":")+1,…
taille(TrChaine)-Position(TrChaine,":"))
Sinon //Pas de séparateur de saisi, on va essayer d'utiliser la taille
TrHeures=gauche(TrChaine,2)
TrMinutes=Milieu(TrChaine,3,2)
Fin
Fin

// Création de la chaîne horaire universelle


TrHeure=NumeriqueVersChaine(TrHeures,"02d")+…
NumeriqueVersChaine(TrMinutes,"02d")

// Comme l'utilisateur peut avoir saisi n'importe quoi, il se peut


// que l'heure trouvée soit invalide, on teste donc :
Si Pas DateValide(TrHeure) alors TrHeure=""

Renvoyer TrHeure

On sait donc traiter une saisie d'heure effectuée à peu prés n'importe comment…

Il faut maintenant aussi être capable de transformer une chaîne horaire universelle pour la présenter
comme le souhaite l'utilisateur.

C'est le but de la fonction GDWHeureVersChaine. Cette dernière se sert d'une variable globale
appelée TrFormatHeure qu'il vous aura suffit d'initialiser au préalable en fonction des choix de
l'utilisateur.

Procedure GDWHeureVersChaine(TrHeure)
// Cette fonction transforme TrHeure (qui contient une heure au format
// universel entre 0000 et 2359) en une chaîne contenant l'heure au
// format local et en utilisant la varaible globale TrFormatHeure comme
// modèle
// TrFormatHeure peut être :
//- HH:MM (Temps universel)
//- HH:MMam (format anglais)

Si Pas HeureValide(TrHeure) ou SansEspace(TrHeure)="" alors Renvoyer ""

TrChaine est une chaîne=TrFormatHeure


TrHeures,TrMinutes sont des entiers
TrHeures=gauche(TrHeure,2)
TrMinutes=milieu(TrHeure,3,2) // On ne prend que le milieu pour le cas
// ou la chaîne passée en parameter contienne
// aussi des secondes et des centièmes (SS:CC)

Si position(TrChaine,"am")>0 alors
// Format Anglais : on doit transformer les valeurs > 12 et
// metre en place les am/pm
Selon TrHeures
cas 0
TrChaine="12:"+NumeriqueVersChaine(TrMinutes,"02d")+"am"
cas 1,2,3,4,5,6,7,8,9,10,11
TrChaine=NumeriqueVersChaine(TrHeures,"02d")+…
":"+NumeriqueVersChaine(TrMinutes,"02d")+"am"
cas 12
TrChaine="12:"+NumeriqueVersChaine(TrMinutes,"02d")+"pm"
cas 13,14,15,16,17,18,19,20,21,22,23
TrChaine=NumeriqueVersChaine((TrHeures-12),"02d")+":"+…
NumeriqueVersChaine(TrMinutes,"02d")+"pm"
Fin
Sinon
// Format universel
TrChaine=NumeriqueVersChaine(TrHeures,"02d")+":"+…
NumeriqueVersChaine(TrMinutes,"02d")
Fin

Renvoyer TrChaine

Et voilà ! Avec ces quelques fonctions, vous devriez pouvoir gérer à peu prés tous les cas possibles
et imaginables…

A vous de jouer !

Vous trouverez de nombreuses procédures ou fonctions de gestion des dates et des heures
dans le chapitre « Plus de 70 Routines ou Fonctions WinDev ! »

oOoOoOoOoOoOoOo
Les Timers avec WinDev

WinDev fournit des fonctions permettant de gérer des timers et vous pouvez aussi appeler des
fonctions de timer de Windows grâce aux API… Mais à quoi ça sert ?

A quoi ça sert ? Comment ça marche ?

Typiquement, un timer va servir à déclencher l'exécution d'une série d'instructions à intervalles


réguliers… Ces instructions peuvent être à peu prés de n'importe quel type, à quelques exceptions
prés que nous aborderons dans le chapitre sur les dangers des interruptions.

Le fonctionnement d'un timer est toujours le même, quelle que soit la méthode utilisée… A un
moment donné (initialisation du projet, d'une fenêtre ou code particulier), on va démarrer un timer…

A ce moment, on indiquera un intervalle de temps (par exemple une seconde) et le code à utiliser (la
procédure ProcTimer par exemple). Toutes les secondes, le code en cours d'exécution sera donc mis
en attente pendant que le code de la procédure ProcTimer sera exécuté. Lors de la sortie de la
procédure ProcTimer, le traitement initial reprend là où il en était.

Timer et Charge CPU

Il n'y a pas si longtemps, de nombreuses informations ont circulé sur la mailing-list WinDev Forum,
concernant l'absorption TOTALE des ressources CPU par l'instruction Timer. Si effectivement à cette
époque on se retrouvait avec le processeur occupé à 100 % quand on utilisait cette instruction, les
choses ont bien changé depuis…

Si vous ne nous croyez pas, testez l'exemple fourni dans le guide (aide en ligne) de WinDev :
Horloges réalisées à l'aide des fonctions de gestion des heures…

Cet exemple utilise un timer réglé à 98/100ème de seconde pour afficher des horloges. Les tests que
nous avons effectués sur un ordinateur équipé d'un simple Celeron 500 (dont vraiment l'entrée de
gamme aujourd'hui) nous ont donné les résultats suivants :

En mode test 32 bits, une fenêtre de menu permettait d'ouvrir :

• soit la fenêtre d'origine de l'exemple (utilisant Timer),

• soit une fenêtre modifiée simplement en remplaçant l'instruction Timer par TimerSys censée
être moins gourmande en ressources processeurs,

• soit une fenêtre modifiée simplement en remplaçant l'instruction Timer par l'appel à une
procédure APITimer inspirée du code fourni à l'époque par Eric Nguyen et dont voici le détail :

Procédure APITimer(TrDuree)

TrHandle est un entier long=0


TrI est un entier sans signé = 1
tantque (TrHandle=0 et TrI<0xffff)
TrHandle=appelDll32("user32", "SetTimer", Handle(), TrI, TrDuree, Null)
TrI++
FIN

Renvoyer TrHandle

Les résultats obtenus avec le moniteur système (fourni en standard dans les accessoires de
Windows) en observant l'occupation CPU ont été :

• Lorsque seule la fenêtre de menu était ouverte, 0 à 2% d'occupation

• Avec la fenêtre Menu et celle utilisant Timer , 3% d'occupation

• Avec la fenêtre Menu et celle utilisant TimerSys, 2 à 3% d'occupation

• Avec le fenêtre Menu et celle utilisant APITimer, 2 à 3% d'occupation

Comme on peut le voir, PCSoft a fortement amélioré la gestion des timers en Wlangage et on en
arrive à un point où il est clair que tout recours aux API semble inutile, en tout cas si on développe un
programme uniquement pour des plates-formes 32 bits (où le nombre de TimerSys n'est
pratiquement pas limité).

Timer, TimerSys, API

Examinons justement quelles sont les différences entre Timer, TimerSys et les appels aux API…

Contrairement à l'instruction Timer gérée en interne, TimerSys fait appels aux Timers de Windows, ce
qui implique deux corollaires :

• La consommation CPU est moins élevée,

• Sur des plates-formes 16 bits, le nombre de TimerSys disponibles est très limité.

Donc pour ceux qui développeraient encore pour Windows 3.x, attention aux TimerSys…

Pour les autres versions de Windows, pas de problème…

Quand on dit que la consommation CPU est moins élevée, il faut replacer les choses dans leur
contexte actuel… Si, il y a quelques années, Timer utilisait 100% du processeur, il est maintenant
beaucoup moins gourmand… La différence entre Timer et TimerSys s'est donc très fortement réduite
et le choix entre les deux instructions est maintenant beaucoup moins important.

Qu'est ce qui pourrait donc encore justifier un appel direct aux API ?

La seule différence qui reste c'est que les Timers de Windows se paramètrent en 1000ème de
secondes alors que ceux de WinDev se paramètrent en 100ème !

Mais ne rêvez pas ! Si PCSoft annonce dans son aide en ligne une précision de l'ordre de +/-5
centièmes de secondes pour ses instructions, il y a une bonne raison… Windows ne passe pas son
temps à attendre votre bon vouloir, il est tout le temps occupé avec la plomberie…

Donc, paramétrer un Timer avec un délai de 973 millièmes de secondes est plus qu'illusoire (pour
autant que cela soit jamais nécessaire).

Timer et débogueur

Très souvent, les instructions déclenchées par Timer sont des instructions de "confort" (affichage de
l'heure, vérification d'autorisation d'utilisation, etc…). Dans ce cas, pensez à utiliser un code
ressemblant à :

Si pas EnModeTest() alors Timer(…

En effet, lors du développement, nous sommes souvent amenés à utiliser le débogueur pour tracer
un traitement ou vérifier des valeurs…

Or, l'utilisation du débogueur peut devenir très frustrante quand un ou des Timers n'arrêtent pas de
vous placer dans un code que vous ne voulez pas inspecter et que vous n'arrivez donc pas à lire /
inspecter / vérifier le morceau de code qui vous intéresse.

Enfin, si vous lisez entièrement l'aide en ligne concernant les Timers (ce qu'il est toujours conseillé de
faire ☺), vous vous apercevrez que la pose d'un point d'arrêt classique (CTRL BREAK ou la pose
d'un point d'arrêt sous l'éditeur) est inopérante dans une procédure appelée par Timer.

Si vous voulez déboguer une procédure de ce type, il faudra y placer l'instruction Stop.

Dangers d'interruptions

Nous avons souvent vu passer des discussions sur le Forum WinDev concernant le fait qu'une
procédure appelée par timer (extrait de l'aide en ligne) "ne doit pas appeler de fonction de gestion de
fichiers (dont le nom commence par "H", HLitRecherche...)."

Certains développeurs ont bien sûr fait des essais et ont constaté que rien n'empêche physiquement
d'effectuer des traitements sur des fichiers dans ces procédures…

Alors pourquoi une telle interdiction ?


Tout simplement parce que vous ne saurez jamais quel traitement a été interrompu par le timer en
question…

Alors imaginons le scénario suivant :

• Votre code en cours de traitement est en train de parcourir le fichier client (entre autres) par
ordre alphabétique pour calculer des statistiques… Au moment de l'interruption, il est
positionné sur le client "Axlinfo"…
• Votre code appelé par Timer a besoin de faire une recherche sur le fichier client et de traiter
des informations concernant le client "Diacom International"…
• Lorsque le traitement général reprend, le "contexte" du fichier client a totalement changé,
l'index est positionné sur un autre enregistrement, le contenu des variables d'état h.numenr et
autre a changé… Votre traitement général, s'il ne se plante pas carrément, va au moins
oublier tous les clients entre Axlinfo et Diacom…

Attention, cet exemple est volontairement très simple, et il est clair qu'on pourrait imaginer des
solutions pour que tout fonctionne bien dans ce cas, comme sauvegarder l'enregistrement courant, la
clé courante, les variables d'état… et tout restaurer à la fin de la procédure appelée par Timer…

Mais comment faire pour restaurer un hFiltre qui pourrait être utilisé dans le traitement principal si on
en a besoin d'un dans la procédure ? Et bien d'autres effets de bord indésirables peuvent survenir
auquel vous ne penserez pas au moment d'écrire votre procédure, tout simplement peut-être parce
que les traitements en cause n'existent pas encore…

Soyez donc TRES PRUDENTS quand vous utilisez les timers… Comme toutes les instructions qui
sont très puissantes, le revers de leur médaille est très dangereux…

Enfin, ne soyez pas trop sûrs de vous quand même… Sachez en effet que certains traitements ne
seront pas interrompus, et ce, malgré votre demande.

En effet, Windows gère des priorités au niveau des différentes tâches en cours, et vous n'arriverez
pas à interrompre des tâches qui ont un niveau de priorité trop grand pour vous.

Ainsi, certaines tâches hard (comme des lectures sur disquettes par exemple) peuvent être longues
et ne peuvent tout simplement PAS être interrompues (pour des raisons physiques tout à fait
logiques).

En conclusion, que faut-il retenir des timers ? En WinDev, tout comme dans les autres langages, ce
sont des fonctionnalités très puissantes à utiliser avec parcimonie, uniquement quand c'est vraiment
nécessaire, et ne pas oublier qu'elles ont aussi leurs limites…

oOoOoOoOoOoOoOo
Création et Utilisation de Classes

Nous considérons ce chapitre comme important, car au vu des demandes sur le Forum WinDev, il
semble que bon nombre d'entre vous s'intéressent à ce sujet, posent beaucoup de questions, mais
ont encore un peu peur de se lancer dans l'écriture de classes WinDev, donc dans la Programmation
Orientée Objet...

Cependant, comme pour marcher (souvenez-vous, lorsque vous étiez un bambin et que vous
marchiez à quatre pattes !), vous pouvez commencer doucement et, au fur et à mesure que les
concepts de la POO vous seront familiers, vous pourrez commencer à courir !

Nous allons donc aborder dans ce chapitre les bases de la POO et, en douceur, écrire quelques
classes WinDev que vous pourrez reprendre éventuellement dans vos applications.

Pour cela, 4 classes différentes vous sont proposées :

1. Classe Température (très simple)


2. Classe Déroulant (un peu plus élaborée...)
3. Classe Dessin et des classes dérivant de la classe Dessin :
a. Classe Cercle
b. Classe Losange

Ces trois dernières classes appliquant des concepts plus avancés de la POO...

vous pouvez exporter l'ensemble de ces classes directement depuis l'interface du Guide…

Quels sont les avantages de la POO et des classes ?

Un des avantages fondamental d'écrire des classes (donc de pratiquer peu ou prou la POO) est tout
d'abord la « ré-utilisabilité » du code.

En effet, lorsqu'une classe a été écrite et déboguée correctement, vous pouvez la réutiliser dans
toutes vos applications, sans être obligé de réécrire une ligne de code de celle-ci.
Par ailleurs, le fait d'utiliser une ou plusieurs classes permet de programmer plus « proprement » car
les données relatives aux tâches dévolues à une classe sont « encapsulées », ce qui évite les effets
de bord en cas de modification malencontreuse de variables...

L'encapsulation est le nom d'un principe, une règle essentielle en programmation objet qui veut que
les données soient systématiquement Privées et manipulables uniquement par des fonctions
Publiques. Ainsi, personne ne peut manipuler de façon hasardeuse les données puisqu'on ne peut
les modifier qu'à travers des fonctions qui peuvent proposer des contrôles afin d'empêcher les
données incohérentes (absurdes).

Par ailleurs, une classe regroupe un certain nombre de procédures ou fonctions, appelées dans le
cas précis « méthodes ». Nous verrons plus loin en détail ce que sont ces méthodes...

Enfin, la POO oblige à respecter certaines conventions d'écriture, ce qui rend le code plus « propre »,
plus facile à lire et à modifier.

Rassurez-vous, nous n'allons pas vous faire un cours savant sur la programmation objet dans le
cadre d'écriture et d'utilisation de classes WinDev.

Notre propos est de démystifier ce sujet et permettre à ceux qui n'ont pas encore franchi le pas de s'y
mettre allègrement, sans complexe ni crainte aucune...

Qu'est ce que c'est qu'un langage objet ?

Les langages objets les plus célèbres sont : C++, SmallTalk, Java.

Ils sont apparus pour modéliser de façon plus efficace. En effet, la programmation structurée
raisonne dans les termes suivants : d'un coté, l'on trouve le code et de l'autre les données
manipulées par le code.

La programmation objet réunit dans un même objet les données et le code.

Pour illustrer cette philosophie, prenons un exemple.

Supposons que l'on veuille modéliser un vélo...

Plutôt que de créer une librairie Vélo avec les données du vélo (taille, pignons, ...) et les fonctions
pour manipuler le vélo (donner un coup de pédale, freiner, ...) on va créer une classe vélo qui va
contenir ces données et ces fonctions.

Par abus de langage, on peut parler de fonctions, mais il vaut mieux parler de Méthodes en
programmation objet.
Bases de la POO

Les restrictions d'accès

Les données et les fonctions contenues dans une classe peuvent se voir imposer des restrictions
d'accès. Ce sont principalement les accès Public et Privé.

Autrement dit, de l'extérieur, on peut appeler une fonction ou manipuler une donnée Publique. Par
contre, une donnée Privée ne peut être manipulée que de l'intérieur de la classe.

Notion d'encapsulation

Comme indiqué précédemment, l'encapsulation est le nom d'un principe, une règle essentielle en
programmation objet, qui veut que les données soient systématiquement Privées et manipulables
uniquement par des fonctions Publiques. Ainsi, personne ne peut manipuler de façon hasardeuse les
données, puisqu'on ne peut les modifier qu'à travers des fonctions qui peuvent proposer des
contrôles afin d'empêcher les données incohérentes.

Notion d'héritage simple

C'est ce qui fait la puissance de la programmation objet, en offrant une réutilisabilité du code.

Supposons notre classe Vélo. On veut maintenant modéliser une Mobylette.. Le vélo et la mobylette
possèdent des points communs et finalement la mobylette n'est qu'un vélo à moteur.

On crée donc une classe Mobylette qui hérite des données et méthodes du Vélo.

Ensuite cette classe ajoutera une donnée Moteur et d'autres données supplémentaires pour faire une
Mobylette. Un moteur peut bien sûr être lui-même une classe Moteur !

On ajoutera également des méthodes pour allumer les phares par exemple.

Si une méthode de la classe Vélo doit être modifiée, on la redéfinit (Surcharge), un appel dans cette
nouvelle méthode de celle de la classe de base étant possible pour n'ajouter que le code manquant.
La programmation objet nous permet donc de créer de nouvelles classes à partir de classes
existantes !

L'héritage multiple

Il consiste à créer une nouvelle classe à partir de plusieurs. Par exemple, on peut créer un vélo
bateau ! Ce mécanisme est très puissant mais peut poser des problèmes pour les concepteurs de
compilateurs. Tandis que le C++ propose un héritage multiple illimité (C++ ne connaît vraiment pas
de barrières d'où sa soi-disant complexité !), Java propose un mécanisme plus limité et plus simple.

Seul l'héritage simple est permis en WinDev. Nous ne parlerons donc pas ici de l'héritage multiple.

Mais vite, je sens que vous commencez à décrocher !

Habituellement, un programme c'est une suite d'instructions. L'ordinateur est très bête et il faut tout lui
détailler :

Prenons un exemple simple d'ouverture d'une porte et l'allumage d'une ampoule en pseudo-code :

- Mettre la main sur la poignée de la porte


- Tourner la poignée
- Pousser la porte
- Mettre le doigt sur l'interrupteur
- Appuyer sur l'interrupteur pour allumer l'ampoule

Tout se passe très bien.

Mais qu'est-ce qui se passe par exemple si on met une porte automatique ?

Le programme sera incapable de trouver la poignée et d'ouvrir la porte !

En programmation objet, on associe aux objets des actions (aussi appelées méthodes).

Par exemple, à l'objet Porte, l'on peut associer la méthode Ouvrir.

De même, pour une ampoule on pourrait associer une méthode allumer, éteindre, etc.

Le programme devient plus simple:

- Porte:Ouvrir
- Ampoule:Allumer

Il n'est plus nécessaire de savoir comment la porte s'ouvre.

On se contente de l'ouvrir...

Pour indiquer qu'on applique la méthode (Ouvrir) sur l'objet (Porte), on noterait en WinDev
Objet : Méthode (ici Porte : Ouvrir).
Pour indiquer qu'on applique la méthode (Allumer) sur l'objet (Ampoule), on noterait en WinDev :
Allumer : Ampoule

Bien sûr, il faut détailler ce que fait la méthode Ouvrir de Porte et la méthode Allumer de Ampoule.

Nous n'allons pas détailler dans le programme ce que fait la méthode Ouvrir, mais nous allons le
détailler dans l'objet lui-même, c'est-à-dire dans la classe.

C'est normal, puisque la méthode Ouvrir ne s'applique qu'à la porte, pas à la lumière:

L'objet Porte et sa méthode Ouvrir

Porte::Ouvrir
Mettre la main sur la poignée
Tourner la poignée
Pousser la porte

On peut changer la porte en mettant une porte automatique.

On peut aussi l'ouvrir (même si la porte elle-même ne s'ouvre pas de la même façon):

L'objet Porte Automatique et sa méthode Ouvrir

Porte ::Ouvrir
Se placer devant la porte
Attendre que la porte soit complètement ouverte

Mais votre programme pourra l'ouvrir sans rien changer:

Le programme principal : il est inchangé malgré le changement de porte et la façon de


l'ouvrir...

Les intérêts primordiaux de la Programmation Orientée Objet

La programmation objet offre plusieurs intérêts, entre autres :

1. Vous pouvez utiliser des objets sans savoir comment ils sont programmés... (c'est le cas de
notre ouverture de porte).
2. Les objets peuvent être modifiés sans avoir à modifier votre programme (c'est aussi le cas ici).
3. Les objets sont facilement réutilisables dans de nouveaux programmes.
4. Les langages objet offrent des mécanismes pour permettre ce genre de programmation.
5. La définition d'un objet et de ses méthodes est appelée Classe. A partir d'une classe, on peut
créer plusieurs objets (plusieurs portes par exemple). Chaque objet créé est appelé instance
de la classe.
Mais ce n'est pas tout : la programmation objet a bien d'autres avantages !

Comme nous venons de le voir, un objet a des attributs (une ampoule a un attribut état qui est allumé
ou éteint.).

Imaginons à présent un objet Température (qui mémorise une température).

On veut pouvoir utiliser la température en degrés Celsius (°C) et en degrés Kelvin (°K).

Pour passer des degrés Celsius aux degrés Kelvin, on ajoute 273.

L'objet température aura donc 2 attributs : TempC et TempK:

Voyons donc comment implémenter cet objet en programmation classique. On va créer une structure
Température :

Temperature est composé de


TempCelsius est un entier
TempKelvin est un entier
Fin
// Affectation des attributs de Temperature
Temperature.TempCelsius=21
Temperature.TempKelvin=294 // (21+273)

Pour connaître la valeur de la température X en degrés Kelvin, on écrit Temperature.TempCelsius,


ce qui donne 21.

Si on veut modifier la température, on peut mettre 33 dans TempCelsius en tapant


Temperature.TempCelsius = 33.

Mais là on a un problème : la température en °K (TempKelvin) ne correspond plus !

Objet Température attributs: TempCelsius = 33 TempKelvin = 294

Il y a une erreur : TempKelvin ne correspond plus à TempCelsius.

Notre objet Temperature n'est plus dans un état correct : il ne fait plus la bonne correspondance entre
°C et °K !
Il faut donc empêcher n'importe qui d'aller modifier TempCelsius et TempKelvin pour ne plus avoir ce
problème.

Heureusement, les langages objets sont capables de protéger certaines données contre des
modifications intempestives.

Pour traiter ce cas en programmation, objet, on déclare la classe Temperature :

Temperature est une classe


Privé
TempCelsius est un entier
TempKelvin est un entier
FIN

Le mot "Privé" dans cette déclaration de la classe signifie que c'est une donnée privée : seul l'objet
Température lui-même peut aller modifier ces données. Ainsi, il est maintenant interdit d'écrire :

Temperature.TempCelsius=21.

Si vous tentez d'exécuter ce code, vous obtiendrez la belle boîte de dialogue ci-dessous :

Nous allons maintenant écrire une méthode dans la classe Température qui permettra de modifier la
température.

Appelons cette méthode RéglerTemperature...

Le code WinDev s'écrira comme ceci :

Procédure TEMPERATURE::ReglerTemperature(nTemp)
:TempCelsius = nTemp
:TempKelvin = :TempCelsius+273

Notez bien les deux points qui précédent l'affectation d'une nouvelle température !

Cela signifie que la variable Privée TempKelvin ne peut être modifiée que via l'intermédiaire de la
variable Privée TempCelsius, grâce à la méthode ReglerTemperature() et au paramètre nTemp.

Il vous donc est impossible de modifier directement TempKelvin, sans modifier TempCelsius, donc de
modifier le rapport existant entre ces 2 valeurs !

A présent, créons deux autres méthodes de la classe Température, l'une pour connaître la valeur en
degrés Celsius, l'autre pour connaître la valeur en degrés Kelvin :

Fonction TEMPERATURE::Celsius()
Privé
X est un entier = :TempCelsius // Notez bien les 2 points précédant TempCelsius
Renvoyer X

Fonction TEMPERATURE::Kelvin()
Privé
x est un entier = :TempKelvin// Notez bien les 2 points précédant TempKelvin
Renvoyer X

Ecrivons à présent ces quelques lignes dans le code d'un potentiomètre :

TP est un objet Temperature


TP:ReglerTemperature(POTEN1)
LIBELLE2..LIBELLE="Celsius : "+TP:Celsius()+" - Kelvin : "+TP:Kelvin()

Placez un libellé nommé LIBELLE2 au-dessus du potentiomètre...

A chaque mouvement du curseur du potentiomètre, vous voyez maintenant les 2 valeurs de


TempCelsius et TempKelvin être modifiées grâce à nos deux méthodes précédentes !

Il est possible de protéger ainsi tous les attributs souhaités grâce à la déclaration Privé !

Il est même possible de déclarer "Privé" des méthodes. Ce sont alors des méthodes internes à l'objet
que seul l'objet lui-même a le droit d'utiliser. "Privé" permet donc de protéger un objet pour éviter qu'il
ne soit modifié ou utilisé n'importe comment.
C'est quoi l'héritage ? C'est quoi le polymorphisme ?

Imaginons que l'on veuille faire une collection de formes géométriques : carrés, cercles, triangles, afin
de pouvoir dessiner des Carrés, des Rectangles, des Cercles, des Triangles, etc.

Quand vous avez un objet sous la main, vous voulez pouvoir le créer ou le déplacer sans avoir
besoin de savoir de quel type d'objet il s'agit.

Problème ! Tous les objets ont des attributs différents et ne se déplacent pas de la même façon,
comme par exemple un Carré ou un Cercle :

La Classe Carré avec ses attributs et La Classe Cercle avec ses attributs
ses méthodes et ses méthodes...

Chaque fois qu'on a un objet, il faut regarder si c'est un carré, un cercle ou un triangle et appeler la
bonne méthode pour le déplacer. Ce n'est pas pratique !

En programmation objet, on a une astuce : l'héritage.

Qu'on en commun les carrés, cercles et triangles ?

Ce sont tous des formes, ils possèdent tous une couleur et on peut tous les déplacer.

Créons donc une classe Forme plus générale :


Cela veut dire : toute Forme a une couleur et on peut la déplacer et changer sa couleur.

Maintenant on va dire que Carré et Cercle héritent (ou "dérivent") de Forme (un carré est une
forme, un cercle est une forme, etc.).

Nos classes Carré et Cercle deviennent alors :

Classe Carré avec ses attributs


et sa méthode Déplace

Classe Cercle avec ses attributs


et sa méthode Déplace

En bon code WinDev, cela s'écrit :

Déclaration de la classe Forme


Couleur est un entier long
posX est un entier long
posY est un entier long
FIN

// Méthode Changer Couleur de la classe Forme


Procédure ::ChangeCouleur(cCouleur)
:Couleur=cCouleur
// Méthode Déplace de la classe Forme
Procédure ::Déplace(dx,dy)

// Déclaration de la classe Carré :


Carre est une Forme
X1 est un entier long
Y1 est un entier long
X2 est un entier long
Y2 est un entier long

// Déclaration de la méthode Déplace de Carré :


Carre::Déplace(dx,dy)
X1 = x1 + dx
Y1 = y1 + dy
X2 = x2 + dx
YZ = y2 + dx

// Déclaration de la classe Cercle


Cercle est une forme
X est un entier long
Y est un entier long

// Déclaration de la méthode Déplace de Cercle :


Cercle::Deplace(dx ;dy)
x = x + dx
y = y + dy

Comme vous le voyez, il n'est pas nécessaire de mettre l'attribut Couleur dans chaque classe :
un cercle est une forme, or une forme a une couleur, donc un cercle a une couleur !
Pour les méthodes, c'est un peu différent :

ChangeCouleur :

On change la couleur de toutes les formes de la même façon. C'est pour cela que c'est dans la
classe Forme qu'on a programmé la méthode ChangeCouleur.

Déplace :

Dans la classe Forme, on a juste indiqué déplace(x,y) : cela signifie que tout objet héritant de Forme
devra pouvoir être déplacé (sans dire comment).

Si vous créez un nouvel objet qui hérite de Forme, vous devrez programmer une méthode déplace
pour ce nouvel objet).

Maintenant, il est possible d'écrire :

Forme :Déplace(5,6)

Là, vous ne savez pas si X est un carré, un cercle ou un triangle. Mais vous pouvez déplacer la
forme, même si vous ne savez pas comment déplacer spécifiquement un carré, un cercle...

Quand vous appelez la méthode "Déplace", le système va regarder de quel objet il s'agit et appeler la
bonne méthode "Déplace" : celle du carré si c'est un carré, celle du cercle si c'est un cercle...

C'est ça le polymorphisme !
C'est très pratique. Par exemple vous voulez créer une liste de carrés, cercles, triangles, etc.

Il suffit de créer un tableau de Formes :

T est un tableau de Forme

(un tableau T de 100 Formes numérotées 1, 2... à 100).

Vous pouvez remplir ce tableau de carrés, cercles, triangles, etc.

Ensuite pour déplacer (par exemple) la 5ème forme du tableau, faites T[5].déplace(5,6).

Vous n'avez pas besoin de savoir de quelle forme il s'agit : vous vous contentez de la déplacer.

Tout ce que vous venez de voir constitue les fondements de base de la programmation objet (ou
programmation orientée objet).

Un exemple fonctionnel d'héritage de classes WinDev

Voici à présent une classe Dessin, totalement fonctionnelle en code WinDev reprenant notre
exemple de la classe Forme, mais en code 100 % WinDev :

Cette classe permet de dessiner dans un champ image des objets : cercles, rectangles, losanges,
etc.

Dessin est une classe


// Un objet de la classe Dessin contient :
// Pour la gestion de l'héritage
// pObjet permet d'accéder à l'objet dérivé (héritage) de Dessin
//
pObjet est un Lien dynamique
// -------------------------------------------
nX,nY sont des entiers //Position
nHaut,nLarg sont des entiers //Taille
nNum est un entier //Numéro
couleur est un entier long //couleur
pPrecedent est un Dessin dynamique //Dessin suivant
fin

Terminaison de classe Dessin

Méthode Dessin::Afficher
// Affiche le dessin sur l'image:
drectangle(:nX,:nY,:nX+:nLarg,:nY+:nHaut,:couleur,inoir) //Un rectangle...
dtexte(:nX+1,:nY+1,numeriqueverschaine(:nNum),iNoir) //Avec le
numéro du dessin dedans

Méthode Dessin::Constructeur
// Constructeur de la classe Dessin
// pPrec est le dernier dessin créé (=NULL si on va créer le 1er dessin)
// x,y,larg,haut définissent la position et la taille du dessin
Procedure Dessin::Constructeur(pPrec,x=1,y=1,larg=20,haut=20)
:nX=x
:nY=y
:nHaut=haut
:nLarg=larg
:couleur=rvb(hasard(255),hasard(255),hasard(255))
// Numéro de l'objet: si ce n'est pas le 1er objet, on ajoute 1 au numéro de l'objet précédent:
si pPrec=NULL alors :nNum=1 sinon :nNum=pPrec:nNum+1

// On place le nouveau dessin en tête de la liste des dessins:


:pPrecedent=pPrec
pPrec=objet

// Gestion de l'héritage
:pObjet = objet

Méthode Dessin::Detruire
// Libère une liste de dessins

pDessin,pAux sont des objets Dessin dynamique

//On va libérer les objets à partir du suivant de l'objet courant:


pDessin=:pPrecedent
tantque pDessin<>NULL
//On retient quel est le dessin courant:
pAux=pDessin
//On avance dans le chaînage:
pDessin=pDessin:pPrecedent
//On libère le dessin courant:
//avec gestion des héritages
libérer pAux:pObjet
fin
// On peut maintenant libérer l'objet courant:
// (Attention:le faire plus haut dans cette méthode peut provoquer un plantage!)
pAux=objet

libérer pAux:pObjet

Méthode Dessin::Deplacer
// Déplace un dessin (ne redessine pas le dessin)
procedure Dessin::Deplacer(x,y)
:nX=x
:nY=y

Créons à présent un objet Cercle hérité de la classe Dessin :

Cercle est une Classe


un objet dessin
Fin

Méthode Cercle::Afficher
// Affiche le dessin sur un champ image :
// Un cercle... Attention à la syntaxe ! Notez bien les :
dCercle(:nX,:nY,:nX+:nLarg,:nY+:nHaut,:couleur,inoir)
// Avec le numéro du dessin dedans
dtexte(:nX+1,:nY+1,numeriqueverschaine(:nNum),iNoir)

Méthode Cercle::constructeur
Procedure Rond::constructeur(pt, aa, bb, cc, dd)
// Appel du constructeur de la classe ancêtre
constructeur dessin(pt, aa, bb, max(cc,dd), max(cc,dd))
// mémorisation de l'adresse de l'objet courant
// dans l'objet hérité
:pObjet = objet

Créons enfin une classe Losange, elle aussi héritée de la classe Dessin :

losange est une Classe


un objet dessin
Fin

Terminaison de classe Losange

Méthode Losange::Afficher
//Affiche le dessin sur l'image:
nX2 est un entier = :nX + :nLarg/2
nY2 est un entier = :nY + :nHaut/2
// Un polygone...Attention à la syntaxe ! Notez bien les :
dpolygone(4, nX2, :nY, :nX + :nLarg, nY2, nX2, :nY+ :nHaut, :nX, nY2, :couleur, inoir)
// Avec le numéro du dessin dedans
dtexte(:nX+1,:nY+1,numeriqueverschaine(:nNum),iNoir)

Méthode Losange::Constructeur
Procedure Losange::constructeur( pt, aa, bb, cc, dd)
// appel du constructeur de la classe ancêtre
constructeur dessin(pt, aa, bb, max ( cc, dd), max ( cc,dd ))
// mémorisation de l'adresse de l'objet courant
// dans l'objet hérité
:pObjet = objet

WinDev et la POO
A présent que nous avons étudié les bases de la programmation objet, qu'en est-il exactement avec
WinDev ?

Principes et bases

La Programmation Orientée Objet permet de développer de façon modulaire. C'est pourquoi les
différents modules d'un projet vont être définis en tant qu'objets.

Vous pouvez donc avoir des objets Client, Graphe, Dessin, Excel, etc.

Il est possible de créer des classes à l'infini, en fonction des besoins exprimés...

Classes et Objets

Chaque module est donc défini sous la forme d'une classe.

Une classe permet de créer toutes sortes d'objets.

A partir de la classe Dessin, vue précédemment, nous avons créé les objets Cercle, Losange, etc.
Méthodes et Membres

Chaque objet possède un ensemble de méthodes et de membres. Les membres et les méthodes
caractérisent donc chaque objet créé à partir d'une classe.

Par exemple, la méthode Afficher() de l'objet Losange permet d'afficher où on le souhaite un


losange...

Les membres d'un objet caractérisent donc un objet. Ils constituent les attributs de cet objet et
peuvent donc être consultés ou modifiés.

Par exemple, le membre Couleur de la classe Dessin permet de changer la couleur d'un cercle, d'un
losange...

Méthodes et membres Globaux

Un membre dit 'Global' d'une classe, appartient à tous les objets de la classe en même temps. Un
tel membre est donc consultable et modifiable depuis n'importe quelle partie du code du projet.

Une méthode dite 'Globale' d'une classe est accessible depuis tous les objets de la classe (comme
une méthode classique ou 'Local') mais peut, en outre, être appelée de n'importe quelle partie du
code du projet.

Il suffit d'ajouter le mot-clé 'Global' devant la déclaration d'un membre ou d'une méthode pour rendre
celui-ci global à la classe.

Exemples :

// Déclaration de la classe
Dessin est une Classe
NX,nY sont des entiers
....
GLOBAL
pDernier est un dessin dynamique
FIN
// Initialisation d'un membre global
Dessin :pDernier = NULL
Prrocédure GLOBALE Dessin ::Détruire
// Libère une suite de dessins
pDessin, pAux sont objets Dessin dynamique

Utilisation d'un membre global

Un membre dit 'Global' doit être utilisé avec les caractères '::' précédant son nom. Si l'appel à ce
membre s'effectue en dehors d'une méthode de la classe, le nom de la classe doit être spécifié.

Dans l'exemple ci-après, le membre 'pDernier' est global à la classe. Il est appelé depuis la méthode
'Constructeur' de la Classe Dessin. Notez la différence entre l'utilisation de 'pDernier' (qui est global
à la classe...) et de 'nNum' qui est quant à lui local :

Procédure Dessin ::Constructeur(x=....


...
SI ::pDernier =NULL Alors
:nNum = 1
Sinon
:nNum = ::pDernier :nNum + 1
Fin

Utilisation d'une méthode 'Globale'

Tout comme les membres globaux à une classe, les méthodes globales doivent être précédées des
caractères ' ::' ainsi que du nom de leur classe.

Dans l'exemple ci-après, l'appel à la méthode globale 'Détruire()' de la classe Dessin s'effectue dans
le code de fermeture de la fenêtre. Notez de plus ici, l'utilisation du membre global 'pDernier' de cette
même classe :

// On termine un dessin dans un champ image


dFinDessin()
// Appel d'une méthode globale
Si Dessin ::pDernier <> NULL Alors Dessin::Détruire()
Durée de vie d'un objet

Chaque objet est une variable et obéit donc aux mêmes règles en terme de durée de vie.

Un objet non déclaré en tant que 'GLOBAL' n'existe donc que dans le bloc de code où il est déclaré.

L'encapsulation des données


Les méthodes et les membres d'une classe peuvent être déclarés avec un attribut qui définit leur
accès.

L'accès de ces éléments détermine qui peut les affecter, les utiliser, les appeler...

L'accès de chaque méthode ou chaque membre peut être :

Public : choix par défaut = aucune restriction,


Protégé : accès restreint au code des classes dérivées,
Privé : accès limité au code de l'objet lui-même.

Déclaration d'accès "Public"

Un membre ou une méthode d'un objet déclaré comme "Public" peut être appelé :
• Depuis l'intérieur de l'objet, par une méthode,
• Depuis l'extérieur de l'objet (par n'importe quel traitement ayant accès à l'objet).

Cet accès permet donc à tous les codes d'utiliser un membre ou une méthode de l'objet.

Exemple :

// Déclaration d'une méthode publique


Fonction Public Cercle::Afficher
Utilisation d'une méthode "Public"

L'appel d'une méthode "Public", toujours précédée du signe ':' (double point) doit contenir le nom de
l'objet à qui appartient cette méthode.

A l'intérieur de l'objet concerné, le nom de l'objet est bien sûr inutile.

// Appel de la méthode Afficher de l'objet Cercle


// hors de l'objet concerné
MonObjet est un objet MaClasse
MonObjet :Afficher()

A l'intérieur de la classe, on supprime la référence à un objet, puisque la méthode Afficher est connue
dans la Classe :

// Appel de la méthode Couleur ()


// par la méthode Afficher()
Fonction MaClasse ::Afficher()
:Couleur() // appel de la fonction

Utilisation d'un membre "Public"

A l'instar d'une méthode d'objet, un membre dit "Public" doit être précédé du nom de l'objet auquel il
appartient ainsi que du caractère ' :'

Là aussi, à l'intérieur de l'objet, le nom de l'objet est inutile.

// Appel du membre Couleur


// hors de l'objet concerné
MonObjet est un objet MaClasse
MonObjet :Couleur=iBleuClair

Dans cet exemple, le membre 'Couleur' appartient en public à la classe MaClasse, on utilise
'MonObjet' pour affecter ce membre en dehors de la classe.
// Appel du membre Couleur
// depuis la méthode Afficher
Fonction MaClasse::Afficher
:Couleur=iRougeClair

La méthode Afficher appartient à la classe MaClasse et peut donc utiliser le membre Couleur en
utilisant uniquement le caractère ' :'

Déclaration d'accès "Protégé"

Une méthode ou un membre peut être déclaré comme "Protégé". En ce cas, on peut appeler cette
méthode ou ce membre :

• Depuis l'intérieur de l'objet (par une méthode),


• Depuis un objet créé à partir d'une classe dérivée de la classe d'origine.

Seuls les objets de la classe concernée et des classes dérivées peuvent donc accéder à un membre
ou à une méthode "Protégé".

Syntaxe de déclaration "Protégé" :

Fonction Protégé MaClasse ::Afficher()

Utilisation d'une méthode "Protégée"

L'appel d'une méthode "Protégée" doit être précédée du caractère ' :' pour l'identifier comme méthode
de l'objet.

L'appel depuis la classe d'Origine ou depuis une classe dérivée utilise la même syntaxe :

// Appel de la méthode Couleur()


// par la méthode Afficher()
Fonction MaClasse ::Afficher()
:Couleur() // C'est une méthode déclarée 'Protégée'

Ici, la méthode Couleur() ne peut être appelée que depuis l'intérieur d'une méthode de la classe
MaClasse ou d'une classe dérivée de maClasse

Utilisation d'un membre "Protégé"

Un membre dit "Protégé" doit être précédé du caractère ' :'' pour pouvoir être utilisé.

L'affectation ou la consultation de la valeur d'un membre 'Protégé' n'est possible que depuis une
méthode de l'objet auquel il appartient ou des objets des classes Dérivées de la classe d'origine.

Exemple :

Dessin est une classe


Protégé
pObjet est un Lien dynamique
...

Dans cette déclaration, le membre pObjet est 'Protégé' et ne peut donc être lu ou modifié que dans la
classe Dessin ou une des classes dérivées de Dessin...

Déclaration d'accès "Privé"

Une méthode ou un membre d'un objet déclaré comme étant 'Privé' ne peut être appelé que depuis
l'intérieur de l'objet auquel il appartient, (par une méthode).

Aucun autre objet ne peut donc accéder à un membre ou une méthode 'Privé', et cette limitation
concerne également les classes dérivées.

Cet accès permet donc de se protéger contre l'accès intempestif à des membres ou des
méthodes propres à l'objet.

// Déclaration d'une méthode privée


Fonction Privé MaClasse::Afficher()
...
Utilisation d'une méthode "Privée"

L'appel d'une méthode Privée étant réservée à l'objet auquel elle appartient, il suffit de faire précéder
le nom de la méthode par le caractère ' :'

Une méthode 'Privée' doit donc être appelée comme ceci :

MonObjet est un objet MaClasse


MonObjet :Afficher()

Utilisation d'un membre "Privé"

Un membre dit 'Privé' doit comme tout membre être précédé du caractère ' :' pour pouvoir être utilisé.

De plus, comme toutes les méthodes 'Privées', les membres 'Privés' ne peuvent être appelés que
depuis l'intérieur de l'objet auquel ils appartiennent.

Héritages de Classes
L'héritage d'une Classe permet de créer une nouvelle Classe possédant les attributs (méthodes et
membres) de la classe ancêtre auxquels s'ajoutent les attributs de la nouvelle classe.

Par exemple, un 'Fils' hérite des attributs de son 'Père' et bénéficie de ses propres attributs.

Comme nous l'avons vu plus haut, la classe Cercle hérite de la classe Dessin et peut donc accéder
aux méthodes et membres de Dessin. On dit alors que Cercle dérive de Dessin.

Déclaration d'une classe héritée

Une classe héritée est définie de la même façon qu'une classe Standard à la différence de la ligne
précisant son ancêtre.
Dans l'exemple ci-dessous, la classe Cercle hérite de la classe Dessin. On voit ici que la
déclaration des membres de la classe ancêtre Dessin n'est pas répétée :

// Déclaration de la classe Cercle


// Cercle est une classe
un objet Dessin
FIN

Utilisation d'une méthode ou d'un membre de classe héritée

Une méthode ou un membre de classe hérité peut être utilisé comme n'importe quel autre élément de
la classe.

L'affectation de valeur à un membre ou l'appel d'une méthode héritée est donc précédé du caractère
' :'

Méthode Cercle::Afficher
// Affiche le dessin sur un champ image :
// Un cercle... Attention à la syntaxe ! Notez bien les :
dCercle(:nX,:nY,:nX+:nLarg,:nY+:nHaut,:couleur,inoir)
// Avec le numéro du dessin dedans
dtexte(:nX+1,:nY+1,numeriqueverschaine(:nNum),iNoir)

Ici, nX, nY.. sont des membres de la classe Dessin qui sont utilisés dans la classe Cercle qui hérite
de Dessin...

Méthode Constructeur
La méthode nommé 'Constructeur' possède la particularité d'être appelée automatiquement lors de
la création d'un objet.
Cette méthode doit donc être utilisée pour initialiser les membres de l'objet qui est créé...
Méthode Dessin::Constructeur
// Constructeur de la classe Dessin
// pPrec est le dernier dessin créé (=NULL si on va créer le 1er dessin)
// x,y,larg,haut définissent la position et la taille du dessin
Procedure Dessin::Constructeur(pPrec,x=1,y=1,larg=20,haut=20)
:nX=x
:nY=y
...

Méthode Destructeur
La méthode nommée 'Destructeur' est également appelée automatiquement lors de la destruction
d'un objet (on parle alors de libération de l'objet...)

Cette méthode est utilisée le plus souvent pour libérer les ressources qui ont été utilisées ou pour
mettre à jour un chaînage.

La méthode 'Destructeur' n'accepte aucun paramètre.

Exemple :

Procédure MaClasse::Destructeur

Libérer Objet1
Libérer Objet2
...

Ici, Objet1 et Objet2 ont été alloués et sont dans cette méthode libérés lors de la destruction de
l'objet.

Redéfinition d'une méthode


Lors de l'héritage d'une classe, il est possible de redéfinir une méthode de la Classe Ancêtre.

La redéfinition d'une méthode permet d'obtenir une méthode dont le nom est conservé mais dont le
fonctionnement est redéfini.

La méthode dérivée aura alors un comportement particulier dans la Classe Dérivée.

Exemple :

1. Méthode Cercle ::Dessiner()

Procédure Cercle ::Dessiner(x,y, Diamètre)


DCercle( x , y + Diamètre, y + Diamètre)

La méthode 'Dessiner' est définie dans la classe Cercle et redéfinie dans la classe Ellipse ci-
dessous :

2. Méthode Ellipse ::Dessiner

Procédure Ellipse ::Dessiner(x, y, hauteur, largeur)


DCercle(x, y, x+hauteur, y+largeur)

Instanciation d'objet dynamique


L'instanciation dynamique permet, comme son nom l'indique, de créer un objet dès qu'il est
nécessaire et de le libérer lorsqu'il n'est plus utilisé.

Les mots-clés utilisés pour ce type d'instanciation sont 'Allouer' et 'Libérer'.

Exemple :

// Code d'ouverture d'une fenêtre


GLOBAL
MonDessin est un objet dynamique

Ici, création d'un objet dynamique, l'objet n'existe pas encore...


Procédure AfficheCercle()
MonDessin = Allouer un Cercle()
MonDessin :Dessiner()
Libérer MonDessin

Dans cette procédure, on alloue un objet dynamique, on appelle la méthode Dessiner de l'objet puis
on libère l'objet dynamique, donc on libère les ressources utilisées...

Méthodes Virtuelles
Les méthodes virtuelles sont des méthodes qui n'ont pas de code dans leur Classe d'origine.

C'est lors d'un héritage que leur fonctionnement sera défini.

Ce mécanisme permet de définir une classe possédant des méthodes dont le fonctionnement sera
propre à chaque classe dérivée.

Exemple :

1. déclaration d'une méthode virtuelle : il n'y a pas de code :

Procédure virtuelle Destructeur()


// ne fait rien de particulier

2. Le code de la méthode virtuelle est défini dans la classe Dérivée :

Procédure Cercle ::Destructeur()


// Efface les cercles ayant été dessinés
:DétruitCercles()

Voilà, nous en avons terminé avec les concepts de la POO.

Il est temps maintenant de passer aux travaux pratiques !


Comment créer une Classe WinDev ?
Les 5 étapes nécessaires

1ère étape : la création de la classe

Ouvrez ou créez une fenêtre WinDev et appuyez sur la combinaison de touches SHIFT+F8.

Le menu contextuel de gestion des classes s'affiche :

Cliquez alors sur le bouton Créer sous la liste Classes...

2ème étape : définition du nom de la classe

Une classe est toujours enregistrée avec l'extension '.WDC'. Il vous faut donc définir :

• Le nom logique de la classe (telle qu'elle sera connue sous WinDev),


• Le nom physique sous lequel elle sera enregistrée.

3ème étape : la déclaration de la classe

Le code de déclaration de la classe permet de définir les membres qui composent une classe.

Cette partie est équivalente à la déclaration de n'importe quelle variable d'un projet.

Le code ci-dessous est généré automatiquement par WinDev :

MaClasse est une Classe

Fin

4ème étape : déclaration des membres de la classe

C'est donc entre ces 2 lignes que vous devez définir les membres de votre classe, comme par
exemple :

Dessin est une classe


// Pour la gestion de l'héritage
// ... permet d'accéder à l'objet dérivé (héritage) de Dessin
//
pObjet est un Lien dynamique
// -------------------------------------------
nX,nY sont des entiers //Position
nHaut,nLarg sont des entiers //Taille
nNum est un entier //Numéro
couleur est un entier long //couleur
pPrecedent est un Dessin dynamique //Dessin suivant
Fin

Les membres de la classe peuvent être de n'importe quel type parmi les types de variables existants
en Wlangage (entier, chaîne, monétaire, date, etc.).
Il s'agit ici de définir les membres qui appartiendront à chaque Objet de la Classe...

L'accès à chaque membre (Public, Privé ou Protégé) peut être ici défini. Un membre peut de plus
être défini comme membre 'GLOBAL' à la classe.

5ème étape : création d'une méthode

La création d'une méthode (ou fonction) de classe s'effectue, elle aussi, par le Menu contextuel. Pour
créer une méthode, ouvrez le menu contextuel des classes :

puis cliquez sur le bouton Créer sous la liste des Méthodes...

Une fois créée, une méthode d'une classe peut être appelée depuis n'importe quel objet de cette
classe. La seule exception à cette règle concerne les méthodes dites Globales à la classe.
Utilisation des ActiveX
Visual Basic de Microsoft a été le premier environnement Iié au développement de programmes à
introduire l'idée de fournir des composants logiciels au marché de masse.

Mais le concept de composants logiciels réutilisables remonte bien plus loin que le Visual Basic, et il
est implanté dans les théories de la programmation orientée objet. Cependant, les langages de POO
n'apportent jamais la réutilisation escomptée, probablement plus à cause de problèmes de marketing
et de standardisation qu'autre chose.

Bien que Visual Basic n'exploite pas complètement la programmation orientée objet, il fournit le
concept de composant par la définition d'un moyen standard de construire et distribuer de nouveaux
contrôles que les développeurs pourraient intégrer à l'environnement. Le premier standard technique
lancé par Visual Basic était le VBX, une spécification entièrement 16 bits toujours disponible dans la
dernière version de WinDev.

En passant aux plates-formes 32 bits, Microsoft a remplacé le standard VBX par les contrôles
ActiveX, plus puissants et plus ouverts.

Qu'est-ce que c'est ?

Les contrôles ActiveX étaient auparavant appelés Contrôles OLE (ou OCX). Le nouveau nom ActiveX
reflète davantage une nouvelle stratégie commerciale de la part de Microsoft qu'une réelle innovation
technique.

Techniquement, ActiveX peut être considéré comme une légère extension de la technologie OCX. Ne
soyez donc pas surpris de voir que les ActiveX s'enregistrent sous forme de fichiers comportant
l'extension OCX.

En utilisant le jargon OLE, on définira un contrôle ActiveX comme étant un "objet document composé
qui est implémenté comme une DLL de serveur en exécution, et qui supporte l'Automation OLE,
l'édition visuelle, et une activation inside-out". C'est plus clair, à présent, non ?

Voyons ce qui se cache derrière cette définition...

Un contrôle ActiveX utilise une approche identique à celle des objets serveurs OLE, qui sont des
objets qu'il vous est possible d'insérer dans un document OLE. La différence entre un serveur OLE
ordinaire et un contrôle ActiveX vient du fait que les serveurs OLE peuvent être implémentés de trois
manières différentes :

• Comme des applications autonomes (par exemple Microsoft Excel),


• Comme des serveurs out-of-process, c'est-à-dire des fichiers exécutables qui ne peuvent se
lancer eux-mêmes, mais qui peuvent uniquement être appelés à partir d'un serveur (par
exemple Microsoft Graph et des applications similaires),
• Comme des serveurs en exécution, telles les DLL chargées sur le même emplacement
mémoire que le programme qui les utilise.

Seule cette dernière technique permet d'implémenter les contrôles ActiveX, c'est d'ailleurs également
la plus rapide. De plus, les contrôles ActiveX sont des serveurs d'automation OLE (traités également
dans cet ouvrage). Cela signifie qu'il vous est possible d'accéder aux propriétés des objets et
d'appeler leurs méthodes. L'interface d'Automation OLE est dépourvue d'évènements, qui ont été
ajoutés à ActiveX.

Vous pouvez voir un contrôle ActiveX dans l'application qui l'utilise et interagit directement dans la
fenêtre contenant l'application : telle est la signification du terme édition visuelle, ou in-place-
activation. Un simple clic active le contrôle, plutôt que le double-clic utilisé par les documents OLE, et
le contrôle est actif aussi longtemps qu'il reste visible (ce que le terme inside-out activation signifie),
sans pour autant à avoir à double-cliquer dessus.

Comme nous l'avons mentionné auparavant, un contrôle ActiveX possède des propriétés, méthodes
et évènements. Les propriétés peuvent être déclarées comme identifiants, mais peuvent aussi activer
des méthodes. (Ce qui est particulièrement vrai pour les contrôles ActiveX qui sont des contrôles VBX
mis à jour, puisque dans un VBX il n'y a aucune autre façon d'activer une méthode que de fixer une
propriété).

Les propriétés peuvent se reporter à des valeurs d'ensemble, des objets, des sous-objets, et ainsi de
suite. Les propriétés peuvent être également dynamiques ou en lecture seule...

Dans un contrôle ActiveX, les propriétés sont divisées en différents groupes : les propriétés de stock
que la plupart des contrôles ont besoin d'implémenter ; les propriétés de fond (ambient) qui offrent
des informations sur le conteneur ; les propriétés étendues gérées par le conteneur, comme par
exemple la position de l'objet ; et enfin les propriétés personnalisées, répondant à vos besoins.

Les évènements et le méthodes sont........eh bien, des évènements et des méthodes. Les
évènements se rapportent à un clic souris, une touche pressée, l'activation d'un composant, et
d'autres actions spécifiques à l'utilisateur. Les méthodes sont des fonctions et des procédures
apparentées à un contrôle. Il n'existe pas de différence majeure entre les concepts ActiveX et
WinDev en ce qui concerne les évènements et les méthodes.

Les contrôles ActiveX sont basés sur la DLL, ce qui signifie que vous aurez besoin de distribuer le
code (le fichier OCX) avec l'application qui l'utilise. Avoir un fichier séparé vous permet de partager le
code entre différentes applications, comme le font normalement les DLL. Si deux applications
partagent le même contrôle (ou paquet d'exécution), vous n'avez besoin que d'une seule copie sur le
disque dur et une autre en mémoire. L'inconvénient, cependant, correspond à l'utilisation par deux
programmes de deux versions différentes d'un contrôle ActiveX, ce qui risque d'engendrer quelques
problèmes de compatibilité.

Installer un contrôle ActiveX

Après avoir installé une librairie de contrôle(s) ActiveX sur votre ordinateur, en suivant les instructions
du fournisseur, vous devez vous assurez de l'enregistrement correct de ce(s) contrôle(s) dans la base
de registres Windows.

Normalement, les programmes d'installations de ces composants le font automatiquement, mais sait-
on jamais, cet enregistrement peut avoir échoué pour une raison ou pour une autre...

Si c'était le cas, utilisez la commande suivante en WinDev :

DdeLance("RegSvr32.EXE Nom_de_lActiveX_à_installer.OCX")

N'omettez pas de préciser le chemin complet de REGSVR32.EXE (en principe dans le répertoire
SYSTEM de Windows, ainsi que le chemin complet où se trouve votre ActiveX.

RegSvr32.Exe permet d'effectuer des modifications dans la base de registres en y enregistrant ou en


supprimant un composant tel qu'une DLL, un ActiveX, etc...
Il comporte certains paramètres optionnels utiles à connaître :

/u = unregister server : supprime l'enregistrement d'un composant,


/s = Silent : n'affiche aucun message tel Succeed ou Error,
/i = appelle Dllinstall avec une commande optionnelle lors de l'appel de /u ,
/n = n'appelle pas DllRegisterServer, cette option doit être utilisée avec /i.

Normalement, dans vos applications utilisant un ou plusieurs contrôles ActiveX, la première chose
que vous devez faire, c'est enregistrer ces contrôles.

Pour cela, écrivez une procédure globale nommée par exemple Init_Ocx, dont le code pourrait être
écrit de la façon suivante :

Procédure Init_Ocx
Local
clFic est une chaine = ""
elResultat est un entier = 0
Si Frep(SysRep(Faux) + "\richtx32.ocx" ) = ""
fCopieFichier(fRepEnCours( ) +"\DATA\RICHTX32.OCX" , SysRep ( Faux ) + "\RICHTX32.OCX" )
fCopieFichier(fRepEnCours( ) + "\DATA\REGSVR32.EXE",SysRep ( Faux ) + "\REGSVR32.EXE")
clFic=SysRep ( Faux) + "\REGSVR32.EXE /s "+SysRep ( Faux ) + "\richtx32.ocx"
Résultat = DdeLance(clFic)
Si elResultat <> 0
Erreur ("Un composant Windows de l'application n'a pu être installé correctement.. "...
+RC+"Veuillez appeler le 01 02 03 04 05, Merci.")
Fin
Fin

dans cet exemple, notez le paramètre optionnel /s, destiné à RegSvr32.exe, afin de n'afficher
aucun message utilisateur, même d'erreur...
Dans notre exemple, les fichiers RegSvr32.Exe et le contrôle ActiveX RichTx32.Ocx se trouvent à
l'origine dans le sous-répertoire \DATA du répertoire où l'application est logée.
Ils sont copiés dans le répertoire SYSTEM de Windows, avant que l'appel à RegSvr32.Exe ne soit
effectué.
Enfin, normalement, dans cet exemple, il conviendrait de copier les librairies complémentaires de cet
ActiveX, comme par exemple RICHED32.DLL, ComCat.Dll, etc... composants indispensables à
l'utilisation correcte de cet ActiveX.

Où installer un contrôle ActiveX ?

D'une façon générale, sauf si vous avez détecté préalablement qu'un contrôle ActiveX du même
nom se trouve déjà dans le répertoire \System de Windows, il est préférable de toujours l'installer
dans ce répertoire, ceci à fin de rapidité, Windows recherchant d'abord les Dll et les composants
Serveurs dans ce répertoire.

Déterminer quels sont les contrôles ActiveX installés sur une machine

Nous vous proposons pour ce faire, deux fonctions WinDev, utilisant la lecture de la base de registres
Windows...

Pour utiliser celles-ci, créer une fenêtre WinDev dans laquelle vous placez une liste, nommée
LISTE1, par exemple.

Dans le code d'initialisation de LISTE1, insérez le code ci-dessous :

Listeajoute("LISTE1",ListeActiveX())

Puis, ajoutez deux procédures (globales ou locales, comme vous l'entendez), nommées
respectivement :

ListeActiveX() et VerifieType()

Voici le source de ces deux procédures :

Procédure ListeActiveX()

//Liste des contrôles Active X présent sur le poste


//Cette procédure retourne une chaîne formatée sous forme de Liste
//Activex1+RC+Activex2+RC+.....
Liste est une chaîne
NomCle est une chaine

//Initialisation du premier parcours de la base de registre


//La Clé parcourue est :
//HKEY_CLASSES_ROOT\CLSID
NomCle = registrepremieresouscle("HKEY_CLASSES_ROOT\CLSID")
Tantque NomCle <> ""
//Pour chaque Sous-Clé, il faut vérifier que cette sous-clé
//est un ActiveX....
Si VerifieType(NomCle) alors //C'est un activeX
Liste = Liste + Registrelit(NomCle,1) + Rc
FIN
NomCle = Registreclesuivante(NomCle)
FIN

Renvoyer Liste

Cette première procédure fait appel à la seconde :

Procédure VerifieType(Cle)
//Vérifie que la Sous clé est un ActiveX
//Il faut parcourir la sous arborescence, si une Sous-Cle est Control
//c'est un ActiveX
SousCle est une chaîne

SousCle = registrepremieresouscle(Cle)
tantque SousCle <> ""
Si Majuscule(SousCle) = Majuscule(Cle)+"\CONTROL" alors
//C'est un Active X
Renvoyer vrai
FIN
SousCle = registreclesuivante(SousCle)
FIN

renvoyer Faux

Il vous suffit alors d'exécuter le code de la fenêtre pour que la totalité des contrôles ActiveX présents
sur la machine sur laquelle est lancé ce code vous soient affichés dans la liste LISTE1.

Outils complémentaires pour la gestion des contrôles ActiveX

Si vous utilisez fréquemment des contrôles ActiveX, il est vrai que RegSvr32.Exe est très
rudimentaire : il ne donne aucune information de quelque nature que ce soit sur l'enregistrement de
tel ou tel composant ActiveX.

PcSoft nous a fourni un outil nommé WdxView.Exe.

Voici ce que le fichier d'aide PcSoft en dit :

Visualiser les informations relatives à un ActiveX


1. Lancez WDXVIEW :
- soit directement par l'exécutable WDXVIEW.EXE
- soit par le menu déroulant "Outils .. Browser d'ActiveX"
- soit par le menu contextuel d'un champ ActiveX intégré dans une fenêtre, option "Browser
d'ActiveX".

2. Sélectionnez un ActiveX dans la liste des ActiveX présents sur le poste.

Remarque : l'icône de recherche permet d'effectuer une sélection de l'ActiveX par mots clés.

3. Les différentes informations relatives à l'ActiveX sont affichées

4. Choisissez l'élément dont vous voulez connaître les caractéristiques (méthode et événement,
propriété ou constantes): sa description s'affiche.

Remarques :
- la description des caractéristiques d'un ActiveX visualisée avec WDXVIEW correspond aux
informations données par le constructeur. Selon les ActiveX, ces informations peuvent être plus
ou moins détaillées.
- un ActiveX ne possède pas obligatoirement des événements, des méthodes, des propriétés, ou des
constantes.
................

Malheureusement, dans la plupart des cas, certaines propriétés ou méthodes d'un contrôle ActiveX
ne sont pas accessibles avec WdxView, comme indiqué en gras dans la partie précédente.

C'est pourquoi l'un des auteurs de cet ouvrage utilise deux outils très intéressants, développés par la
Société 4Developpers LLC : ActiveX Manager et COM Explorer. Vous pouvez trouver ces outils
sur le site : www.componentsource.com <http://www.componentsource.com>

ActiveX Manager remplit le rôle joué par le programme Microsoft RegSv32.exe, mais plus encore !
Vous pouvez enregistrer directement un ActiveX (ou une DLL..) avec ActiveX Manager, la
désenregistrer, etc...

A la différence de WdxView de PcSoft, il vous indique très clairement l'intégralité des composants
enregistrés dans la base de registres de votre ordinateur.

Par ailleurs, si un composant est enregistré mais est manquant (il a été déplacé, supprimé, etc...)
ActiveX Manager vous l'indique également, comme l'indique la figure ci-dessous :

De plus, ce programme fournit des informations complémentaires très utiles aux développeurs
utilisant un ou plusieurs contrôles ActiveX : détails sur l'ActiveX lui-même, informations sur
l'emplacement, la taille, la date de création du composant, etc...

Vous avez également l'ID du programme, tel qu'il est enregistré dans la base de registres, ainsi que
la valeur de la clé correspondante.

Pour toutes ces raisons, nous vous recommandons fortement ActiveX Manager qui est beaucoup
plus bavard que WdxView !

COM Explorer

COM Explorer est également une application développée par 4Developpers LLC...

Ce programme vous permet de gérer les ActiveX, les DLL, et les serveurs EXE Windows (out-
process) et les DLL serveurs (n-Proc). Pour chacun de ces objets, vous pouvez les informations le
concernant comme :
• La valeur de la clé dans la base de registres,
• Le type de librairie,
• Le numéro de version,
• Mais aussi et surtout les dépendances du composant avec d'autres composants telles que
des DLL Windows, etc...

Cet outil unique est vraiment indispensable si vous utilisez des composants liés à la technologie COM
de Microsoft. Vous apprendrez ainsi une foule de choses sur ces composants

Ces deux applications doivent faire partie de la "caisse à outils" de tout bon développeur utilisant des
composants externes à ses applications. Leur prix modique les met à la portée de toutes les bourses,
même les plus plates !

Vous les trouverez sur Internet à : www.componentsource.com

Enfin, pour être complet avec ces outils, précisons (par expérience...) que l'application WdxView de
PcSoft n'affiche pas TOUTES les propriétés ou méthodes de certains contrôles ActiveX.

C'est pourquoi, si en parallèle de WinDev, vous utilisez (peu ou prou...) Visual Basic, préférez
amplement l'utilisation de l'Explorateur d'Objets qui saura tout vous dire sur tel ou tel contrôle
ActiveX : en effet, il affiche les classes, propriétés, méthodes, événements et constantes disponibles
dans les bibliothèques d'objets et les procédures de votre projet. Il vous permet de rechercher et
d'utiliser des objets que vous créez ainsi que des objets provenant d'autres applications.

Programmation d'un contrôle Activex


Il y a deux méthodes pour programmer un contrôle Activex :

• Développement d'une classe WinDev,


• Programmation simple en faisant appels aux propriétés, méthodes et évènements du contrôle.

Nous traiterons ici de la deuxième méthode, le développement de classes WinDev étant traité dans
un chapitre séparé...

Tout d'abord, vous devez insérer le contrôle voulu dans votre application en lui donnant un nom.

Ceci fait (voir ci-après « exemple d'implémentation d'un contrôle Activex »), si le contrôle possède
des propriétés modifiables hors « run-time », vous pouvez le faire lors de l'insertion du contrôle dans
votre application...

Imaginons que le nom donné au contrôle soit « POP ».

Vous pourrez alors :

Action Syntaxe
• Modifier les propriétés du contrôle Pop>>Propriété = Valeur
• Lire un propriété du contrôle Valeur = Pop>>Propriété
• Appeler une méthode du contrôle Pop>>Méthode() // sans paramètre,
• Pop>>Méthode(Param1...) // avec paramètre,
• * Intercepter un Evènement ActiveXEvenement(NomdeProcédure, NomduChamp,
NomEvenement)

* Les noms des évènements gérés par le contrôle sont indiqués dans la documentation du contrôle...

Notez particulièrement les signes « >> » utilisés pour appeler les propriétés ou les méthodes du
contrôle...

Exemple de gestion d'un évènement :

// Déclaration de l'évènement gérant le double clic (DblClick)


// à l'ouverture de la fenêtre utilisant l'Activex
NumEve est un entier long
NumEve = ActiveXEvenement("DbleClic", "Pop","DblClick")

// Procédure appelée lorsque l'évènement se produit


Procedure DbleClic()
Info("Vous avez exécuté un double-clic")

A noter que l'évènement peut envoyer des paramètres. Dans ce cas, il faut les déclarer au début de
la procédure :

Procedure DbleClic(Param1,Param2)

N'oubliez pas également, lors de la fermeture de la fenêtre contenant votre code de supprimer le lien
à votre contrôle par :

FinEvenement(NumEve)
Bien entendu, il vous est nécessaire AVANT de programmer le contrôle ActiveX de connaître
parfaitement toutes les propriétés, méthodes et évènements !

Enfin, n'omettez pas de distribuer TOUS les composants nécessaires à la bonne utilisation du
contrôle, lors de l'installation de votre application sur les machines de vos clients.

Pour illustrer plus amplement ce sujet, vous trouverez ci-après la méthodologie, pas à pas,
concernant l'implémentation d'un contrôle et sa programmation.

Exemple d'implémentation d'un contrôle ActiveX

Afin d'illustrer ce qui précède, nous vous proposons un exemple d'utilisation (simple), d'un contrôle
ActiveX, celui de Microsoft Contrôle Calendrier, Version 9.0, ainsi dénommé sous WdxView, (and
in english, et sous ActiveX Manager : Microsoft Calendar Control 9.0...).

• Créez une nouvelle fenêtre sous WinDev en tapant CRTL-N, et choisissez le type 'Fenêtre
vierge'
• Enregistrez cette fenêtre sous le nom de Calendrier, ou tout autre nom qui vous plaira, en
sachant que c'est par ce nom que vous allez pouvoir appeler les méthodes et les propriétés
du contrôle, c'est pourquoi un nom court est préférable : il est plus facile à retenir lorsque l'on
écrit une classe WinDev pour piloter le composant,
• Cliquez sur la barre d'outils de WinDev et sélectionnez un Champ ActiveX/OCX en cliquant
dessus, puis cliquez dans la fenêtre WinDev pour initialiser le nouveau champ,
• Dans le menu de description du champ ActiveX, donnez un nom au contrôle, puis cliquez sur
la combo 'ActiveX' afin d'ouvrir la liste des ActiveX enregistrés sur votre ordinateur,
• Sélectionner le contrôle « Contrôle Calendrier 9.0 », (automatiquement, WinDev reconnaît le
contrôle comme un contrôle en 32 bits
• Ne sélectionnez pas « Mode d'exécution 16 bits », laissez-le sur 32 bits,
• Cliquez ensuite sur le bouton « Propriétés 32 bits » : vous avez accès aux propriétés du
contrôle, à travers une fenêtre comportant trois onglets :
Chaque onglet représente un groupe de propriétés, comme expliqué auparavant...

• Modifiez toutes les propriétés que vous voulez, puis n'oubliez pas de cliquez sur le bouton «
Appliquer » de la fenêtre des propriétés du composant,
• Le composant est à présent activé, dans votre fenêtre WinDev...

Vous pouvez le redimensionner à votre guise et le placer où vous le souhaitez, en fonction des
besoins de votre application...

Mais vous pouvez aussi avoir plusieurs contrôles ActiveX (identiques ou différents, comme dans
Visual Basic...) présents dans une seule et même fenêtre (ce qui n'a pas beaucoup d'intérêt dans ce
cas précis !)

Ci-dessous, dans une fenêtre WinDev, nous avons trois fois le même composant, mais avec des
propriétés différentes ; couleurs, polices, etc. :
Voilà, ça y est, le composant ActiveX est à présent prêt à être piloté par vos soins !

ce contrôle ActiveX n'a pas de propriétés selon WdxView.... C'est faux, car le fichier d'aide
MSCAL.HLP référence les propriétés suivantes :

Le tableau ci-dessous décrit la totalité des propriétés du contrôle Calendrier.

Propriété Description
CouleurFond (BackColor) Couleur de fond du contrôle Calendrier.
Jour (Day) Jour en cours sélectionné du mois.
PoliceJour (DayFont) Police utilisée pour l'affichage des jours de la semaine dans le
contrôle Calendrier.
CouleurPoliceJour (DayFontColor) Couleur utilisée pour l'affichage des jours de la semaine.
LongueurJour (DayLength) Format utilisé pour afficher les jours de la semaine.
PremierJour (FirstDay) Premier jour de la semaine qui apparaît dans le contrôle
Calendrier.
AspectCellulesGrille (GridCellEffect) Apparence utilisée pour l'affichage du calendrier.
PoliceGrille (GridFont) Police utilisée pour afficher les jours du mois dans la grille.
Couleur police grille (GridFontColor) Couleur de la police utilisée pour l'affichage des jours du mois.
CouleurQuadrillage (GridLinesColor) Couleur du quadrillage pour un calendrier à deux dimensions.
Mois (Month) Mois en cours affiché dans le contrôle Calendrier.
LongueurMois (MonthLength) Format utilisé pour afficher les mois de l'année.
AffichSélecteursDate Affichage de zones de liste pour la sélection du mois et de l'année.
(ShowDateSelectors)
AffichJours (ShowDays) Affichage des jours de la semaine.
AffichGrilleHorizontale Affichage des lignes horizontales de la grille dans un calendrier à deux
dimensions.
(ShowHorizontalGrid)
AfficherTitre (ShowTitle) Affichage du titre mois/année.
AffichGrilleVerticale (ShowVerticalGrid) Affichage des lignes verticales de la grille dans un calendrier à deux
dimensions.
PoliceTitre (TitleFont) Police utilisée pour afficher le titre mois/année au-dessus de la
grille du calendrier.
CouleurPoliceTitre (TitleFontColor) Couleur de la police utilisée pour l'affichage du titre mois/année.
Valeur (Value) Valeur de la date correspondant à la date sélectionnée dans le
contrôle Calendrier.
ValeurEstNull (ValueIsNull) Aspect de la date en cours, à savoir si elle est en surbrillance ou non.
Année (Year) Année sélectionnée en cours.

Donc, méfiez-vous de ce qu'affiche WdxView, ce n'est pas toujours la réalité....

Comment programmer un contrôle ActiveX ?

Bien évidemment, l'intérêt majeur d'un contrôle ActiveX, c'est de pouvoir programmer en WinDev le
code nécessaire pour que le contrôle réponde à nos attentes et besoins ainsi que ceux des
utilisateurs de nos belles applications.

Vous avez deux méthodes pour piloter un contrôle ActiveX :

1. programmer directement le contrôle par des procédures et la gestion des évènements,


2. développer une classe spécifique au contrôle implémenté.

La première méthode a l'avantage essentiel d'être rapide à mettre en oeuvre et de pouvoir tester
interactivement les propriétés et méthodes du contrôle implémenté. L'inconvénient essentiel est qu'à
chaque nouveau projet utilisant le même contrôle ActiveX, vous devrez reprendre, procédure par
procédure, objet par objet, tout le code précédemment programmé.

La méthode de développement d'une classe spécifique permet de réutiliser à l'infini le même contrôle,
sans avoir à ré-écrire quoi que ce soit...

Par ailleurs, selon vos nécessités, il n'est pas forcément utile de programmer toutes les méthodes et
toutes les propriétés du contrôle. Vous pouvez programmer uniquement ce dont vous avez besoin.
Ainsi, vous ne développez que le strict nécessaire et n'alourdissez pas inutilement le code de votre
classe.

Si vous souhaitez développer directement des procédures vous permettant d'agir sur le contrôle, il
vous faut connaître les propriétés et les méthodes supportées par le contrôle.
Pour le cas qui nous occupe, consulter le fichier MSCAL.HLP qui donne toutes les indications utiles à
cet égard, notamment la syntaxe à utiliser, pour l'appel des méthodes, les paramètres à passer, etc.

1. pour modifier une propriété du contrôle ActiveX :

Pour changer la couleur de fond du contrôle, par exemple, utilisez la syntaxe suivante :

Calendrier >> BackColor = iJauneClair

Décortiquons cette ligne de code :

• Calendrier est le nom donné au contrôle dans votre fenêtre et référencé comme tel
par WinDev,
• >> est l'appel à la propriété BackColor du contrôle
• Le signe = (égal) est utilisé ici et non pas ( ) , car l'on modifie une propriété du contrôle
ActiveX et non pas sur une méthode de celui-ci.
• et iJauneClair le paramètre donné à la propriété.

Vous pouvez ainsi modifier toutes les propriétés d'un contrôle ActiveX en connaissant celles-ci et les
paramètres à passer, sauf pour les propriétés en lecture seule n'étant accessibles que lors de
l'implémentation du composant sous WinDev.

2. pour appeler une méthode du contrôle ActiveX :

Pour illustrer notre propos, nous allons appeler la méthode NextMonth du contrôle, qui a pour effet
d'augmenter d'un mois la valeur d'un contrôle Calendrier lorsque l'utilisateur clique sur le bouton
intitulé AffichMoisSuiv.

Voici le code d'appel de la méthode :

Calendrier >> NextMonth()


Lors de l'exécution de cet appel, la liste déroulante des mois est incrémentée de 1...

Si vous appliquez la méthode NextMonth lorsque la valeur du contrôle est Décembre, l'année est
changée automatiquement. Lorsque le contrôle Calendrier est actif, vous pouvez exécuter cette
méthode en appuyant sur les touches CTRL + DROITE.

Cette méthode n'accepte pas de paramètre et ne renvoie aucune valeur ni code d'erreur...

ici, la notation d'écriture utilisée préserve l'orthographe exacte du nom de la méthode, qui est
une combinaison de majuscules / minuscules. Cependant, cette syntaxe n'a, en réalité, pas
d'importance, l'appel à la méthode fonctionnant tout aussi bien si : nextmonth ou NEXTMONTH. Nous
vous conseillons néanmoins de conserver cette notation, ne serait-ce que pour une meilleure lisibilité
de votre code.

Mais certaines méthodes (ou propriétés...) demandent un ou plusieurs paramètres dans la notation
d'appel, comme par exemple dans la propriété ShowDateSelectors, qui indique au contrôle d'afficher
ou non une zone de liste déroulante pour les mois et les années du contrôle.

Cette propriété demande en paramètre une valeur de type Booléen. Si vous attribuez Vrai (True) à la
propriété ShowDateSelectors, la zone de liste déroulante s'affiche dans le coin supérieur droit du
contrôle Calendrier, si la zone de liste déroulante est active lors de l'activation du contrôle, vous
pouvez masquer cette liste déroulante par la syntaxe suivante :

Calendrier >> ShowDateSelectors = 0

3. pour gérer un évènement du contrôle ActiveX :

La plupart des contrôles ActiveX, à travers des évènements, permettent d'interagir sur la saisie des
utilisateurs et de connaître à tout moment ce qui vient d'être frappé au clavier, cliqué, double-cliqué,
etc...

Dans notre exemple, nous allons récupérer, à chaque clic sur le contrôle Calendrier, quelle est la
date sur laquelle l'utilisateur a cliqué.
Placez l'appel à l'évènement dans le code d'ouverture de la fenêtre :

EXTERN "WINCONST.wl"
Evenement("DbClick","Calendrier",WM_LBUTTONDBLCLK)

Ici, l'évènement est basé sur un double-clic (WM_LBUTTONDBLCLK) (la valeur de


WM_LBUTTONDBLCLK étant récupérée par la lecture du fichier "WINCONST.wl", qui, nous le rappelons,
contient les valeurs des constantes Windows...) de la souris dans le contrôle Calendrier, la procédure
associée étant la procédure DbClick...

Voici le code de la procédure associée DbClick :

Procédure DbClick()
jDay, mMonth, yYear sont des entiers longs =0
Date est une chaine = “”
jDay = Calendrier >> Day() // on récupère le Jour cliqué,
mMonth = Calendrier >> Month() // on récupère le Mois cliqué,
yYear = Calendrier >> Year() // on récupère l'année.
// Transformation en chaine
Si jDay <10
Date = “0”+VersChaine(jDay)+”/”
Sinon
Date = VersChaine(jDay)+ "/"
Fin
Si mMonth < 10
Date=Date+"0"+VersChaine(mMonth)+"/"
Sinon
Date=Date+VersChaine(mMonth)+"/"
FIN
Date=Date+VersChaine(yYear)

Vous aimerez peut-être aussi