ClapMAJ - Utilisation

De Wiki de Lapalys
Révision datée du 22 février 2015 à 20:29 par HFDASysop (discussion | contributions) (→‎Liens utiles)
(diff) ← Version précédente | Voir la version actuelle (diff) | Version suivante → (diff)
Aller à la navigation Aller à la recherche

Ce composant permet finalement des fonctions équivalentes à celles proposées nativement par WinDev et son LiveUpdate. Les différences se situent à plusieurs niveaux :

  • Le composant ne fait pas de mise à jour "réseau" et ne gère pas les déconnexions des utilisateurs (vous pouvez néanmoins programmer ce type de fonctionnalité);
  • Le numéro de version du fichier plus récent n'est pas disponible quand on utilise le HTTP ou le FTP (seul le fichier existe sur ce type de serveur et on ne peut l'ouvrir sans l'avoir téléchargé);
  • Le composant permet de faire une mise à jour par FTP (LiveUpdate ne le permet pas);
  • Le composant peut afficher facilement une jauge pendant le téléchargement (avec % de progression);
  • Au moment de la mise à jour, votre application ne redémarre qu'une seule fois (contrairement à l'instruction AppliDéclencheMAJ);
  • Le composant n'utilise pas de fichier annexe (comme le WDUPDATE.NET) si du côté du "serveur", ni du côté de l'application;
  • Le composant propose plus de statut pour le fichier en train d'être mis à jour (voir les constantes ::_clapMAJÉtatxxx);
  • Le composant peut mettre à jour tout type de fichier (le LiveUpdate ne met à jour que l'exécutable).

Le fichier INI

La fonction de vérification du composant est complètement paramétrable vis-à-vis des chemins d'accès aux fichiers à jour. Pour faciliter la vie du développeur, le composant peut aussi utiliser des données extérieures pour fonctionner. Pour cela, il a été fait le choix d'utiliser un fichier INI dans lequel tous les paramètres peuvent se trouver, sous la section [Update]. La structure des données attendues est expliquée ci-dessous. La connexion HTTP peut utiliser un proxy.

[Update]

Folder = NomDeVotreRépertoire


HTTP = NomDeVotreServeurHTTP (*)

HTTPUser = NomUtilisateurServeurHTTP (*, optionnel)

HTTPPass = MotDePasseServeurHTTP (*, optionnel)

HTTPTimeOut = TimeOutServeurHTTP (en millisecondes, optionnel, par défaut = 5000)

HTTPPing = 1 ou 0 (1 = un ping du serveur se fera avant toute connexion, optionnel, par défaut = 1)


Proxy = NomDeVotreServeurProxy (*)

ProxyUser = NomUtilisateurServeurProxy (*, optionnel)

ProxyPass = MotDePasseServeurProxy (*, optionnel)

ProxyPort = PortDeConnexionProxy (optionnel, par défaut : 8080)


FTP = NomDeVotreServeurFTP (*)

FTPUser = NomUtilisateurServeurFTP (*, optionnel)

FTPPass = MotDePasseServeurFTP (*, optionnel)

FTPPort = PortDeConnexionFTP (optionnel, par défaut : 21)

FTPActive = 1 ou 0 (type de connexion FTP : 1=active et 0=passive, optionnel, par défaut = 0)

FTPTimeOut = TimeOutServeurFTP (en secondes, optionnel, par défaut = 20)

FTPPing = 1 ou 0 (1 = un ping du serveur se fera avant toute connexion, optionnel, par défaut = 1)


Les informations sensibles (marquées d'une étoile) peuvent être cryptées dans le fichier INI. Pour écrire dans le fichier INI les informations cryptées, utilisez la code suivant dans votre programme (les deux derniers paramètres ont de l'importance) :

MonParamètre est une chaine = Crypte(MaValeurNonCryptée,MonMotDePasse,crypteRapide,encodePCS)

Le mot de passe MoMotDePasse utilisé devra alors être passé en paramètre de la déclaration de la classe.


Téléchargement en tâche de fond

Pour que le composant puisse télécharger en tâche de fond, des threads sont utilisés dans la classe clapMAJ (en HTTP et FTP). Le nom du thread est paramétrable dès l'appel de la classe (voir les entrées-sorties du composant). Il sera suffixé, par clapMAJ, par un indice interne. Les threads déclarés dans cette classe sont automatiquement fermés si le logiciel s'arrête ou si l'instance de la classe est fermée ou libérée.

Du fait de l'utilisation des threads, il est conseillé de mettre la ligne suivante dans l'initialisation de votre projet :

ThreadMode(threadSectionCritique)


Exemple de code : procédure local à la fenêtre principale pour vérifier si une mise-à-jour est disponible (à rendre automatique : lancer 1 fois, immédiatement, exécution en tâche de fond sans copie de contexte HF)

PROCEDURE AttendMAJ()
sNomExe est une chaîne = ExeInfo(exeNom)
sFichierINI est une chaîne = fRepExe()+"\monlogiciel.ini"
sPasseIni est une chaîne = "12345"	//à configurer avec votre propre mot de passe si vous cryptez les données sensibles
clMAJ est un objet clapMAJ(sFichierINI,sPasseIni)
BOUCLE
	SI Gauche(gdhTestMAJ,14)<Gauche(DateSys()+HeureSys(),14) ALORS 		//AAAAMMJJHHMMSS
		SI PAS clMAJ:nEtatDeLaMAJ(sNomExe) DANS (clapMAJ::_clapMAJÉtatEnVérif,clapMAJ::_clapMAJÉtatEnCoursDeRéception,clapMAJ::_clapMAJÉtatPrête) ALORS MAJVérifie()
		gdhTestMAJ..Minute+=30	//le prochain test est fait dans 30 minutes
	FIN
	ThreadPause(100)	//pause de 1 secondes entre chaque vérification
FIN

MAJVérifie est une procédure qui permet de télécharger la mise à jour et de l'installer (voir ci-dessous).

gdhTestMAJ est une variable globale au projet :

gdhTestMAJ est une DateHeure = DateSys()+HeureSys()

gdhTestMAJ..Seconde+=30 //le premier test est fait 30 sec après le démarrage du programme

Attention, comme cette procédure est lancé, grâce au paramétrage proposé, dans un thread, il faut veiller à la quitter avant la fermeture de l'application (utiliser l'instruction FinAutomatismeProcédure(AttendMAJ) ).

Télécharger en tâche de fond avec WinDev 15 et inférieur

Selon ce qui est décrit ci-dessus, le composant peut télécharger un fichier en tâche de fond. Cela évite de bloquer le programme au complet pendant cette tâche.

Pour continuer le code appelant tout en téléchargeant un fichier, le composant clapMAJ utilise des thread et envoie, au thread appelant un ThreadEnvoieSignal(). Ce thread appelant est, dans l'exemple ci-dessus, la procédure AttendMAJ (qui va ensuite appeler MAJVérifie qui, lui-même appelle les fonctions de la classe qui font télécharger le fichier) qui s'exécute en tâche de fond pour vérifier régulièrement si une mise à jour est disponible.

Il est facile de paramétrer cette procédure, grâce au bouton [1:30] en haut à droite du code, pour qu'elle s'exécute en tâche de fond. Dans ce cas, c'est WinDev qui fait la gestion du thread, et donc lui donne un nom. Ce nom va être utile dans le composant comme paramètre dans ThreadEnvoieSignal(). Le nom, donné par WinDev et donc non maîtrisé, peut être récupéré par l'instruction ThreadCourant() mais celle-ci n’apparaît qu'en version 16 !

Que faire dans les versions inférieures ? Il faut simplement éviter d'utiliser le bouton [1:30] pour exécuter la procédure automatiquement en tâche de fond mais gérer le thread par programmation. Il suffit donc de laisser la procédure telle quelle, sans exécution particulière, et l'appeler, à l'ouverture de la fenêtre principale, par cette ligne :

ThreadExécute("thMAJ",threadNormal,AttendMAJ,"thMAJ")

De cette manière, le nom du thread (ici "thMAJ") est bien connu et peut être utilisé dans le composant. Dans ce cas, au lieu d'arrêter ce thread par FinAutomatismeProcédure(AttendMAJ), il suffira de l'arrêter avec ThreadArrête("thMAJ"). Il faut bien évidemment adapter la procédure AttendMAJ pour réceptionner ce nouveau paramètre (le "thMAJ" à la fin) et qu'elle le passe à MAJVérifie() qui va finalement l'utiliser lors des appels au composant. Le projet d'exemple proposé sur ce wiki est programmé de cette façon-ci.

Notez que cette gestion "manuelle" des thread peut être utilisé dans toutes les versions de WinDev et fonctionne donc en version 16 et supérieure.


État du téléchargement en temps réel

La méthode :bÉtatDeLaMAJ() permet de connaître le statut d'un fichier dont la mise à jour a été demandée via une instance de la classe ClapMAJ. Cette méthode peut, par exemple, être appelée dans une procédure lancée au démarrage d'une fenêtre. Pour cela, dans la procédure, ouvrez la fenêtre "automatisme de la procédure" (icône 1:30 dans le titre de la procédure) et cochez l'option "Exécution en tâche de fond" et "Sans utilisation de Hyper File" dans le bas de cette fenêtre.

Si cette fonctionnalité est utilisée, merci de vous reporter au paragraphe précédent pour l'instruction ThreadMode() sous peine de voir l'affichage du statut s'arrêter.

Exemple de code : La procédure ci-dessous est locale à la fenêtre principale qui permet d'afficher l'état de la mise à jour du logiciel dans un champ de saisie SAI_MAJ (on peut remplacer le champ de saisie par une case de barre de message).

PROCEDURE AfficheMAJ()
sNomExe est une chaîne = ExeInfo(exeNom)
nÉtatCourant est un entier = clapMAJ::_clapMAJÉtatPasEncoreVérifiée
sFichierINI est une chaîne = fRepExe()+"\monlogiciel.ini"
sPasseIni est une chaîne = "12345"	//à configurer avec votre propre mot de passe si vous cryptez les données sensibles
sRépertoireMAJ est une chaîne = fRepertoireTemp()
cMAJ est un objet clapMAJ()	//les paramètres du constructeur ne sont pas indispensables dans ce cas puisqu'on ne fait ici que de la vérification d'état des fichiers à mettre à jour. Le téléchargement est fait ailleurs.
nÉtatMAJ est un entier
BOUCLE
	nÉtatMAJ=cMAJ:nEtatDeLaMAJ(sNomExe)
	SI nÉtatMAJ<>nÉtatCourant ALORS
		SELON nÉtatMAJ
			CAS clMAJ::_clapMAJÉtatEnVérif : SAI_MAJ=gImage("MAJ_16_1.png")+" Vérifie mise-à-jour..."	//mettre SAI_MAJ="" pour éviter l'apparition intempestive d'icône lors d'une vérification
			CAS clMAJ::_clapMAJÉtatÉchouée : SAI_MAJ=gImage("MAJ_fail_16_1.png")+" Erreur de mise-à-jour"
			CAS clMAJ::_clapMAJÉtatExisteSurFTP,clMAJ::_clapMAJÉtatExisteSurHTTP,clMAJ::_clapMAJÉtatExisteSurRep: SAI_MAJ=gImage("MAJ_16_1.png")+" Mise-à-jour disponible"	
			CAS clMAJ::_clapMAJÉtatEnCoursDeRéception : SAI_MAJ=gImage("MAJ_16_1.png")+" Mise-à-jour en réception"
			CAS clMAJ::_clapMAJÉtatPrête : 	SAI_MAJ=gImage("MAJ_16_1.png")+" Mise-à-jour prête"
			AUTRE CAS : SAI_MAJ=""
		FIN
		nÉtatCourant=nÉtatMAJ
	SINON
		SI PAS nÉtatMAJ DANS (clMAJ::_clapMAJÉtatEnVérif,clMAJ::_clapMAJÉtatExisteSurFTP,clMAJ::_clapMAJÉtatExisteSurHTTP,clMAJ::_clapMAJÉtatExisteSurRep,clMAJ::_clapMAJÉtatEnCoursDeRéception,clMAJ::_clapMAJÉtatPrête) ALORS
			SAI_MAJ=""
		FIN
	FIN
	ThreadPause(15)
FIN


Multi-fichiers

Cette classe est développée pour la mise-à-jour de plusieurs fichiers. En effet, en utilisant une ou plusieurs instances de la classe, l'on peut appeler plusieurs fois la méthode nVérifieSiExiste() avec, en paramètre, le nom d'un fichier. Tous les appels de cette méthode mettra dans une mémoire, partagée entre toutes les instances de la classe, le nom des fichiers appelés et leur état en temps réel. Dans le cas où le même fichier est appelé, son statut repasse en clMAJ::_clapMAJÉtatPasEncoreVérifiée et une nouvelle vérification commence. Le nom d'un fichier n'existe qu'une seule fois en mémoire.

Ceci est notamment utile lorsqu'on veut mettre à jour l'exécutable et ses dll. Dans ce cas, l'on ne peut pas remplacer ces fichiers tant qu'ils sont en exécution. Il est alors proposé la méthode suivante :

  • appeler nVérifieSiExiste() de l'exécutable en cours
  • appeler ensuite, pour toutes les dll, la même méthode de classe
  • appeler enfin la méthode bApplique() avec l'option "bTout" = 1 pour que la classe ferme le programme, copie les versions récentes de l'exécutable et de ses dll dans le répertoire d'exécution et relance le programme après (si demandé par l'option bRelance = 1).

En tout temps, on peut lire l'ensemble des fichiers demandés et leur état grâce à la méthode :sListeMAJ(). L'on peut aussi vider cette mémoire en utilisant la méthode :ListeSupprimeTout() ou supprimer un seul fichier de cette mémoire grâce à la méthode :ListeSupprime(NomDuFichier).

Critères de mise-à-jour

Le composant, pour éviter d'utiliser des fichiers annexes (comme un WDUpdate.NET utilisé avec la méthode proposée dans WinDev), compare des informations du fichier à mettre à jour avec le fichier distant (qu'il soit sur HTTP, FTP ou dans un répertoire).

En premier, si la version du fichier est accessible (c'est le cas s'il est dans un répertoire), la composant vérifie que le fichier distant est d'une version supérieure au fichier local. Si les deux versions sont égales, un test supplémentaire est fait sur la taille du fichier. Les informations de version peuvent être récupérés des fichiers .EXE, .DLL, .WDK (composant PC SOFT) et .WDL (librairie PC SOFT).

Si la version n'est pas accessible, le composant teste si une différence de date, d'heure ou de taille est détectée, la méthode :nVérifieSiExiste renverra la valeur ::_clapMAJÉtatPrête ou commencera à télécharger le fichier selon l'option choisie dans la méthode :nVérifieSiExiste.


Remarque sur les dates et heures

Particulièrement dans le cas du FTP, si l'ordinateur téléchargeant une mise-à-jour n'est pas sur les mêmes date et heure que le serveur, la date et heure du fichier téléchargé pourraient ne pas être ceux attendus.

Exemple : en utilisant un logiciel FTP (p.ex.FileZilla) et en listant les fichiers sur un serveur en Angleterre, on voit la date et heure d'un fichier à "2013-01-15 16:45". La date et heure du même fichier, vu depuis WinDev (donc de ce composant) sera "2013-01-15 15:45". Le composant se charge d'appliquer, au fichier téléchargé, la même date et heure que celle vue sur le serveur selon WinDev. Selon ce principe, le critère de comparaison date/heure est toujours utilisable.

En effet, s'il n'y a pas de mise à jour à faire, aucun fichier ne doit être déposé sur le serveur. Le logiciel ne voyant rien, passe son chemin. Dans l'optique d'une mise-à-jour, l'on dépose un nouveau fichier sur le serveur. Ce nouveau fichier, quelque soit sa date et son heure est forcément celui qui doit faire la mise à jour sur le poste en local. Le logiciel va faire ses comparaisons et télécharger ce nouveau fichier. Ce dernier recevant la date et heure tels que vus sur le serveur, la prochaine comparaison entre la version locale et celle du serveur sera identique.


Exemple de code pour faire une mise à jour

gnÉtatMAJ est une variable globale de type Entier (permet de savoir si la vérification n'est pas déjà lancée dans un autre thread ou avoir l'état de la mise à jour disponible de manière globale)

PROCEDURE MAJVérifie()
sNomExe est une chaîne = ExeInfo(exeNom)
sFichierINI est une chaîne = fRepExe()+"\monlogiciel.ini"
sPasseIni est une chaîne = "12345"	//à configurer avec votre propre mot de passe si vous cryptez les données sensibles
sRépertoireMAJ est une chaîne = fRepertoireTemp()
nAncienEtat est un entier
nTimeOut est un entier

SI PAS gnÉtatMAJ DANS (clapMAJ::_clapMAJÉtatEnVérif,clapMAJ::_clapMAJÉtatEnCoursDeRéception) ALORS	//on vérifie qu'on n'est pas déjà en train de le télécharger
	gnÉtatMAJ=clapMAJ::_clapMAJÉtatEnVérif
	cMAJ est un clapMAJ(sFichierINI,sPasseIni,sRépertoireMAJ)	//on a fait le choix d'avoir les informations dans le fichier INI
	gnÉtatMAJ=cMAJ:nVérifieSiExiste(sNomExe,clapMAJ::_clapMAJSourceToutes,Vrai,Faux,ThreadCourant())	//Télécharge sans jauge
	TANTQUE gnÉtatMAJ DANS (clapMAJ::_clapMAJÉtatEnVérif,clapMAJ::_clapMAJÉtatEnCoursDeRéception)
		gnÉtatMAJ=cMAJ:nEtatDeLaMAJ(sNomExe);nTimeOut++
		SI nTimeOut>=100 ALORS SORTIR SINON Multitâche(-20)
	FIN
	
	SELON gnÉtatMAJ
		CAS clapMAJ::_clapMAJÉtatÉchouée : 
			Erreur("Une erreur est survenue durant la mise à jour :",cMAJ:m_sErreur)
		CAS clapMAJ::_clapMAJÉtatPrête :
			nAncienEtat=gnÉtatMAJ

			Info("Une mise-à-jour va être installée.","Cliquez sur OK pour continuer.")
			
			INIEcrit("Update","LastVersion",ExeInfo(exeVersion)+"-"+fTaille(ExeInfo(exeNom))+"-"+fDateHeure(ExeInfo(exeNom)),sFichierINI)	//permet de vérifier que la mise-à-jour a été faite après avoir relancé le logiciel
			
			Sablier(Faux)
			SI PAS cMAJ:bApplique(sNomExe,Vrai) ALORS
				INIEcrit("Update","LastVersion","",sFichierINI)
				gnÉtatMAJ=nAncienÉtat
				Erreur(cMAJ:m_sErreur)
			FIN
		CAS clapMAJ::_clapMAJÉtatEnVérif
			Avertissement("Temps de vérification de la mise à jour est dépassé.")
		AUTRES CAS
	FIN
FIN


Contraintes techniques

Gestion des droits / Pare-feu / Anti-virus

Puisque la mise-à-jour de l'exécutable se fait par simple déplacement de fichier (depuis le répertoire de temporaire vers le répertoire d'exécution du logiciel), il est possible de les droits de l'utilisateur en cours dans Windows ne soient pas suffisants. La copie va alors échouer et l'ancienne version réapparaîtra.

Pour éviter cela, il y a deux méthodes :

  • l'utilisateur possède les droits administrateurs sur l'ensemble du répertoire du logiciel (au cas où plusieurs fichiers seraient mis à jour).
  • mettre à jour, non l'exécutable mais le fichier .WDL (voir astuces ci-dessous).

De même, la plupart des anti-virus vont se déclencher après que le logiciel ait été relancé. Ceci est normal puisqu'un exécutable peut avoir été changé. L'utilisateur doit donc avoir le droit d'accepter la boîte de dialogue permettant à l'anti-virus de mettre la nouvelle version du logiciel dans sa liste de confiance.

Pour tous les autres fichiers, aucune contrainte particulière n'est à signaler.

Nom des fichiers

Puisque les accès aux "sources" (répertoire, FTP et HTTP) est unique, sans notion de sous-répertoire, les fichiers contenus dans ces sources doivent avoir un nom unique.

De même, comme les fichiers sont téléchargés à la même place (voir le paramètre <sRépertoireMAJ> du constructeur de la classe), ils doivent également avoir un nom unique, sous peine d'écraser un téléchargement par un autre.


Astuces

  • Pour mettre à jour le fichier .WDL plutôt que l'exécutable (sur lequel il y a plus de blocage à cause des droits ou de l'anti-virus), il faut créer votre logiciel avec la fonction suivante : Étape "Définition de l'exécutable" / "Mode d'utilisation de la bibliothèque principale" / "Utilisation externe de la bibliothèque principale".
  • Les DLL identifiées WD*.dll et les fichiers *.WDL : si parmi les fichiers à mettre à jour se trouvent des dll standard ou les librairies de WinDev, celle-ci sont traitées comme si l'on met à jour l'exécutable. En effet, ces dll ou ces librairies ne peuvent être remplacées durant l'exécution du logiciel (qui les utilise). Dans le cas de ces fichiers, l'application va devoir redémarrer (mettre la valeur "vrai" dans le paramètre "bRelance" de la fonction cMAJ:bApplique() ). Si ce paramètre est à faux, les fichiers ne peuvent pas être remplacés et seuls les autres fichiers seront mis à jour.
  • Pour avertir l'utilisateur de votre logiciel que la mise-à-jour a échouée (ou a été faite correctement) : vous pouvez sauvegarder (dans le fichier INI, la base de registre, ...) la version en cours : utilisez pour cela les fonctions ExeInfo(exeVersion) et/ou fTaille(ExeInfo(exeNom)) et/ou fDateHeure(ExeInfo(exeNom),"",fModification) juste avant d'utiliser la fonction cMAJ:bApplique(). Vous comparez cette(ces) valeur(s) avec les mêmes valeurs de l'exécutable après redémarrage de l'application. Si il y a des différences, la mise-à-jour s'est bien passé, sinon affichez un message d'erreur.
  • Pour mettre à jour d'autres fichiers : vous pouvez ouvrir d'autres instances de clapMAJ dans votre programme pour vérifier d'autres fichiers. Attention, tout fichier dont on demande la mise à jour s'ajoute dans la même file d'attente (interne au composant) que ceux appelés par les autres instances du composant. L'utilisation de cMAJ:bApplique() met à jour l'ensemble des fichiers demandés. Vous pouvez avoir plus de liberté en utilisant l'astuce ci-dessous. Aussi, veillez à synchroniser la mise à jour de vos fichiers car le logiciel pourrait redémarrer alors que le téléchargement ou le déplacement d'autres fichiers seraient fait à ce moment précis.
  • Après avoir mis à jour l'exécutable d'une application, et si celle-ci avait été préalablement installée par un assistant d'installation (standard WinDev), veillez à modifier les clés de registre suivantes afin de garder un affichage cohérent. Cette opération peut être faite au démarrage du logiciel, juste après l'avoir mis à jour (voir la seconde astuce ci-dessus pour déterminé si la mise-à-jour s'est bien passée).

Ci-dessous, NomDeLaCléDuProgramme représente l'identifiant de l'application dans la base de registre (voir étape "Répertoire d'installation" de l'assistant de création d'installation de Windev, bouton [Avancé])

HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Uninstall\NomDeLaCléDuProgramme

HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\NomDeLaCléDuProgramme

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\NomDeLaCléDuProgramme


sClé est une Chaîne = UneDes3clésCiDessus
RegistreEcrit(sClé,"DisplayVersion",ExeInfo(exeVersion))
RegistreEcrit(sClé,"MajorVersion",ExtraitChaîne(ExeInfo(exeVersion),1,"."))
RegistreEcrit(sClé,"MinorVersion",Milieu(ExeInfo(exeVersion),Position(ExeInfo(exeVersion),".")+1))


Liens utiles

Les entrées/sorties du composant

Téléchargement et licence