Logiciels    Wiki     Programmation     Electronique     Projets     Auteur 
Retour : Accueil > Programmation > Conseils

Application qui s'auto-installe

Introduction

On trouve sur le marché de nombreux logiciels permettant de créer une procédure d'installation de votre programme, qui copient vos fichiers dans un dossier spécial, modifient éventuellement la base de registre etc. Les produits BORLAND C++Builder et Delphi contiennent d'ailleurs un tel logiciel nommé InstallShield. Cependant ces logiciels présentent un inconvénient qu'est la taille du programme d'installation. Bien sûr ces logiciels compressent tous vos fichiers mais ils rajoutent également leur propre interface graphique qui prend beaucoup de place. Par exemple pour InstallShield il faut compter environ 400ko en plus de vos fichiers non compressés … D'autres logiciels donnent de meilleurs résultats mais je n'ai pas réussi à en trouver un gratuit et qui permette de modifier la base de registre.

C'est pourquoi j'ai déjà voulu utiliser les fichiers INF, qui ont justement pour but de créer des scripts d'installation simples. J'ai dû les coupler à des instructions dans le programme pour avoir plus de possibilités, mais finalement ceux-ci ne marchent qu'à moitié et je me suis rendu compte qu'il était bien plus simple et efficace de programmer complètement l'installation dans le programme même.

L'utilisateur doit simplement décompresser les fichiers dans le dossier dans lequel il souhaite installer votre programme, puis exécuter le programme qui s'installera tout seul. L'utilisateur peut également déplacer le logiciel puisqu'il le détectera et se réinstallera automatiquement à la première exécution, et la désinstallation peut se faire à travers la boîte de dialogue "Ajout/Suppression de programmes" de Windows, ou par une commande dans votre logiciel.

Cette description s'applique toujours à BORLAND C++Builder et Delphi, et les exemples de codes seront comme d'habitude en vert foncé pour C++Builder et en rouge foncé pour Delphi.

Outils nécessaires

Accéder à la base de registre

Il est très facile d'accéder à la base de registre de Windows avec Delphi et C++Builder, qui proposent des routines dédiées.

Il faut tout d'abord inclure la bibliothèque Registry :
#include <registry.hpp>
uses registry;

Puis créer un objet TRegistry :
TRegistry *reg = new TRegistry;
Var reg: TRegistry;
reg := TRegistry.Create;

et l'utiliser :
reg->RootKey = HKEY_LOCAL_MACHINE; // ou HKEY_CLASSES_ROOT, HKEY_CURRENT_USER etc ...
reg->OpenKey(nomClé, CréerSiExistePas?);
reg->WriteString(nomValeur, valeur);
valeur = reg->ReadString(nomValeur);
reg->CloseKey();
reg.RootKey := HKEY_LOCAL_MACHINE;
reg.OpenKey(nomClé, CréerSiExistePas?);
reg.WriteString(nomValeur, valeur);
valeur := reg.ReadString(nomValeur);
reg.CloseKey;

Récupérer les paramètres de la ligne de commande

Delphi et C++Builder fournissent une fonction qui permet de récupérer les valeurs passées en paramètres à l'application à l'exécution : ParamStr(numParam).

La valeur ParamStr(0) contient toujours le chemin complet d'où a été lancée l'application (chemin + nom exécutable).
Les valeurs suivantes contiennent les paramètres suivants qui ont été passés à l'application. La fonction ParamCount() renvoie le nombre de paramètres qui ont été passés à l'application.

Gestion de fichiers

Effacer un fichier
DeleteFile(nomFichier);
DeleteFile(nomFichier);
Cependant vous ne pourrez pas supprimer l'exécutable puisqu'il est en cours d'exécution. Le mieux est de proposer à l'utilisateur de lui ouvrir le répertoire d'installation pour les supprimer manuellement.

Déplacer un fichier
MoveFile(nomFichier1,nomFichier2); // nomFichier.c_str() si nomFichier  variable AnsiString (et pas constante "")
MoveFile(nomFichier1,nomFichier2); // PChar(nomFichier) si nomFichier variable String (et pas constante '')
Copier un fichier
CopyFile(nomFichier1,nomFichier2); // nomFichier.c_str
CopyFile(nomFichier1,nomFichier2); // PChar(NomFichier)
Renommer un fichier
RenameFile(nomFichier1,nomFichier2);
RenameFile(nomFichier1,nomFichier2);
Executer un fichier
#include <shellAPI.h>
ShellExecute(0, NULL, nomFichier, NULL, NULL, SW_NORMAL);
uses shellAPI;
ShellExecute(0, nil, nomFichier, nil, nil, SW_NORMAL);
Attention, si nomFichier est une variable chaîne (et pas une constante), vous devez la convertir en tableau de char :
nomFichier.c_str()
PChar(nomFichier)
Vous pouvez d'ailleurs utiliser cette commande pour une adresse email (en la précédant de 'mailto:'), ou d'une adresse internet (en la précédant de 'http://').

Autres

Boites de message
Il peut être intéressant de demander confirmation, ou d'informer l'utilisateur sur le déroulement des opérations :
if (MessageDlg("Voulez-vous effacer les entrées du registre ?", mtConfirmation,  mbYesNoCancel, 0) == mrCancel) Application->Terminate();
ShowMessage("L'opération s'est bien déroulée");
If MessageDlg('Voulez-vous effacer les entrées du registre ?', mtConfirmation,  mbYesNoCancel, 0) = mrCancel Then Application.Terminate;
ShowMessage('L''opération s'est bien déroulée');
Consultez l'aide pour le détail des paramètres possibles. Icônes (ressources)
Il est possible d'inclure plusieurs icônes dans l'exécutable, dont on peut se servir par exemple pour registrer un type de fichier. Il faut pour cela créer un fichier ressource .res à l'aide de l'éditeur d'image, accessible par le menu Outils, puis créer une ou plusieurs icônes.
Il faut ensuite inclure ce fichier ressource au programme, en ajoutant dans le code de la fiche principale :
#pragma resource "icone.res"
{$R fichier.res}

L'installation / désinstallation et le registre

Pour installer correctement une application, il est nécessaire d'écrire dans plusieurs clés, permettant à Windows de savoir que l'application existe, où elle se trouve, comment la désinstaller, si des types de fichier lui sont associés ...
Nous allons ici détailler où se trouvent ces clés et ce qu'elles doivent contenir.
Vous pouvez observer le contenu du registre grâce à l'Editeur du registre fourni avec Windows, que vous pouvez lancer en tapant 'regedit' dans la boîte Exécuter du  Menu Démarrer.

L'emplacement de l'application

La clé se trouve dans 'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\NomExecutable'.
NomExecutable est le nom de l'exécutable de votre application, AVEC l'extension .exe, par exemple 'CR-KryptX.exe'.

Il faut écrire deux valeurs dans cette clé, une valeur 'Path' qui contient le dossier d'installation (que l'on peut obtenir par ExtractFilePath(ParamStr(0)), et dans la valeur Défaut (que l'on adresse avec un nom de valeur chaîne nulle '' ou "") le chemin complet de l'exécutable (que l'on peut obtenir par ParamStr(0)).

On se servira de cette clé pour déterminer si l'application a été déplacée et par conséquent réeffectuer l'installation.

La désinstallation

On peut rendre possible la désinstallation par l'intermédiaire de la boîte 'Ajout/Suppression de programmes' de Windows simplement en l'enregistrant dans le registre.
La clé se trouve dans 'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\NomApplication'.

Il faut écrire deux valeurs dans cette clé, une valeur 'DisplayName' qui contient le nom qui sera affiché dans la boîte, et une valeur 'UninstallString' qui contient la ligne de commande à exécuter quand l'utilisateur voudra désinstaller l'application. Dans notre cas elle sera composée du chemin et nom de l'application (ParamStr(0)) suivi du paramètre 'uninstall' dont on se servira dans l'application pour savoir qu'il faut désinstaller l'application.

La registration de ses propres types de fichiers

Grâce au registre, on peut également registrer ses propres types de fichiers, c'est à dire choisir une extension et affecter aux fichiers une icône, faire en sorte que par un double clic dessus votre application soit lancée automatiquement, ajouter une commande au menu contextuel permettant d'ouvrir un certain type de fichier avec votre application, bref, donner une allure professionnelle à vos logiciels.

Toutes les registrations de fichiers se trouvent dans la clé principale HKEY_CLASSES_ROOT.
Il y a ensuite une sous-clé par extension registrée, de la forme '.kpx', qui contient dans sa valeur Défaut le nom d'une autre sous-clé, par exemple fKryptX, qui se trouve toujours dans la même clé principale et dans laquelle se trouvent toutes les informations.

On écrira dans cette clé :
   - la valeur Défaut : la description du type de fichier tel que vous voulez qu'il apparaisse dans l'explorateur, tel que 'Fichier crypté CR-KryptX'.
   - la sous-clé 'DefaultIcon' contenant :
      - la valeur Défaut : l'emplacement de l'icône, par exemple le nom de l'application suivi de l'index de l'icône (voir section suivante).
   - la sous-clé 'Shell' contenant :
      - une sous-clé de nom de votre choix de l'action à faire sur le fichier (par exemple 'Décrypter') contenant :
         - la valeur Défaut : le nom de l'action tel qu'il apparaîtra dans le menu contextuel
         - la sous-clé 'command' contenant :
         - la valeur Défaut : la ligne de commande pour exécuter votre application : le chemin et nom de votre application suivi de différents paramètres de votre choix (par exemple 'decrypter') et surtout de "%1" (avec les guillemets) qui renvoie en paramètre le nom du fichier à traiter.
   - la valeur Défaut : facultative, cette valeur contient l'ordre d'affichage des différentes actions (les noms des sous-clés d'actions séparés par des virgules), la première étant celle par défaut qui est exécutée lors d'un double-clic. Cette valeur est à modifier surtout si votre application est la seule à utliser ce type de fichier afin qu'un double-clic l'exécute.

On peut aussi noter quelques autres sous-clés de HKEY_CLASSES_ROOT importantes de types de fichier, comme la clé '*' qui contient les actions affichées dans le menu contextuel pour tous les types de fichiers, la clé 'unknown' qui concerne tous les types de fichiers non registrées, et la clé 'Folder' pour les répertoires.

Dans tous les cas le mieux pour vous donner une idée est de regarder comment sont faites les clés de types de fichiers connus.

L'implémentation finale (exemple de code complet)

  void Installer()
    {
      TRegistry *reg = new TRegistry;
      try
      {
        reg->RootKey = HKEY_LOCAL_MACHINE;
        if (!reg->OpenKey("\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\AppPaths\\CR-KryptX.exe", false)
            || (ParamStr(0) != reg->ReadString("")))
        {//-- si pas installé du tout ou changé chemin
          // chemin
          reg->CloseKey();
          reg->OpenKey("\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\AppPaths\\CR-KryptX.exe", true);
          reg->WriteString("", ParamStr(0));
          reg->WriteString("Path", CheminApp);
          reg->CloseKey();
          // uninstall
          reg->OpenKey("\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall", false);
          reg->CreateKey("\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\CR-KryptX");
          reg->OpenKey("\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\CR-KryptX", false);
          reg->WriteString("DisplayName", "CR-KryptX 1.0");
          reg->WriteString("UninstallString", ParamStr(0)  + " uninstall");
          reg->CloseKey();
          // registrer .kpx et option crypter pour tous les autres fichiers
          reg->RootKey = HKEY_CLASSES_ROOT;
          reg->OpenKey("\\*\\Shell\\CR-KryptX", true);
          reg->WriteString("", "Crypter avec CR-KryptX");
          reg->CloseKey(); reg->OpenKey("\\*\\Shell\\CR-KryptX\\command", true);
          reg->WriteString("", "\"" + ParamStr(0) + "\"" + " crypter \"%1\"");
          reg->CloseKey(); reg->OpenKey("\\.kpx", true);
          reg->WriteString("", "fKryptX");
          reg->CloseKey(); reg->OpenKey("\\fKryptX", true);
          reg->WriteString("", "Fichier crypté CR-KryptX");
          reg->CloseKey(); reg->OpenKey("\\fKryptX\\DefaultIcon", true);
          reg->WriteString("", ParamStr(0) + ",1");
          reg->CloseKey(); reg->OpenKey("\\fKryptX\\Shell", true);
          reg->WriteString("", "Decrypter");
          reg->OpenKey("\\fKryptX\\Shell\\Decrypter", true);
          reg->WriteString("", "Décrypter avec CR-KryptX");
          reg->CloseKey(); reg->OpenKey("\\fKryptX\\Shell\\Decrypter\\command", true);
          reg->WriteString("", "\"" + ParamStr(0) + "\"" + " decrypter \"%1\"");
          reg->CloseKey();
          // clé options
          if (!reg->KeyExists("\\SOFTWARE\\CR-TEKnologies")) reg->CreateKey("SOFTWARE\\CR-TEKnologies");
          if (!reg->KeyExists("\\SOFTWARE\\CR-TEKnologies\\KryptX")) reg->CreateKey("SOFTWARE\\CR-TEKnologies\\XFonte");
          reg->OpenKey("\\SOFTWARE\\CR-TEKnologies\\KryptX", true);
          if (!reg->ValueExists("Langue")) reg->WriteString("Langue", "Français");
          reg->CloseKey();
        }
        reg->OpenKey("\\SOFTWARE\\CR-TEKnologies\\KryptX", true);
        Langue = reg->ReadString("Langue");
      } __finally { delete reg; }
    }

    void __fastcall TForm1::FormCreate(TObject *Sender)
    {
      installer();

      if (ParamStr(1) == "decrypter" && LowerCase(ExtractFileExt(ParamStr(2))) == ".kpx")
      {
        ...
        Application->Terminate();
      } else
      if (ParamStr(1) == "crypter")
      {
        ...
        Application->Terminate();
      }
    }

    void desinstaller()
    {
      int res = MessageDlg("Voulez-vous effacer les entrées du registre  ?", mtConfirmation, mbYesNoCancel, 0);
      if (res == mrCancel) return;
      if (res == mrYes)
      {
        TRegistry *reg = new TRegistry;
        try
        {
          reg->RootKey = HKEY_LOCAL_MACHINE;
          reg->DeleteKey("\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\AppPaths\\CR-KryptX.exe");
          reg->DeleteKey("\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\CR-KryptX");
          reg->DeleteKey("\\SOFTWARE\\CR-TEKnologies\\KryptX");
          reg->OpenKey("\\SOFTWARE\\CR-TEKnologies", true);
          TRegKeyInfo infos; reg->GetKeyInfo(infos);
          if (infos.NumSubKeys == 0) reg->DeleteKey("\\SOFTWARE\\CR-TEKnologies");
        } __finally { delete reg; }
      }

      res = MessageDlg("Vous devez supprimer les fichiers manuellement. Voulez-vous accéder au dossier ?", mtConfirmation, mbYesNoCancel, 0);
      if (res == mrCancel) return;
      if (res == mrYes) ShellExecute(0, NULL, CheminApp.c_str(), NULL, NULL, SW_NORMAL);
      Application->Terminate();
    }

    // dans le source du projet (.cpp)
    WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
    {
      try
      {
        Application->Initialize();
       if (ParamStr(1) == "uninstall") desinstaller();
        ...
    ...

Procedure Installer();
  Var
    reg: TRegistry;
  Begin
    reg := TRegistry.Create;
    Try
      reg.RootKey := HKEY_LOCAL_MACHINE;
      If (Not(reg.OpenKey('\SOFTWARE\Microsoft\Windows\CurrentVersion\AppPaths\CR-KryptX.exe', false))
          Or (ParamStr(0) <> reg.ReadString(''))) Then
      Begin //-- si pas installé du tout ou changé chemin
        // chemin
        reg.CloseKey();
        reg.OpenKey('\SOFTWARE\Microsoft\Windows\CurrentVersion\AppPaths\CR-KryptX.exe', true);
        reg.WriteString('', ParamStr(0));
        reg.WriteString('Path', CheminApp);
        reg.CloseKey();
        // uninstall
        reg.OpenKey('\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall', false);
        reg.CreateKey('\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\CR-KryptX');
        reg.OpenKey('\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\CR-KryptX', false);
        reg.WriteString('DisplayName', 'CR-KryptX 1.0');
        reg.WriteString('UninstallString', ParamStr(0) + ' uninstall');
        reg.CloseKey();
        // registrer .kpx et option crypter pour tous les autres fichiers
        reg.RootKey := HKEY_CLASSES_ROOT;
        reg.OpenKey('\*\Shell\CR-KryptX', true);
        reg.WriteString('', 'Crypter avec CR-KryptX');
        reg.CloseKey(); reg.OpenKey('\*\Shell\CR-KryptX\command', true);
        reg.WriteString('', '"' + ParamStr(0) + '"'  + ' crypter "%1"');
        reg.CloseKey(); reg.OpenKey('\.kpx', true);
        reg.WriteString('', 'fKryptX');
        reg.CloseKey(); reg.OpenKey('\fKryptX', true);
        reg.WriteString('', 'Fichier crypté CR-KryptX');
        reg.CloseKey(); reg.OpenKey('\fKryptX\DefaultIcon', true);
        reg.WriteString('', ParamStr(0) + ',1');
        reg.CloseKey(); reg.OpenKey('\fKryptX\Shell', true);
        reg.WriteString('', 'Decrypter');
        reg.OpenKey('\fKryptX\Shell\Decrypter', true);
        reg.WriteString('', 'Décrypter avec CR-KryptX');
        reg.CloseKey(); reg.OpenKey('\fKryptX\Shell\Decrypter\command', true);
        reg.WriteString('', '"' + ParamStr(0) + '"'  + ' decrypter "%1"');
        reg.CloseKey();
        // clé options
        If Not(reg.KeyExists('\SOFTWARE\CR-TEKnologies')) Then reg.CreateKey('SOFTWARE\CR-TEKnologies');
        If Not(reg.KeyExists('\SOFTWARE\CR-TEKnologies\KryptX')) Then reg.CreateKey('SOFTWARE\CR-TEKnologies\XFonte');
        reg.OpenKey('\SOFTWARE\CR-TEKnologies\KryptX', true);
        If Not(reg.ValueExists('Langue')) Then reg.WriteString('Langue', 'Français');
        reg.CloseKey();
      End;
      reg.OpenKey('\SOFTWARE\CR-TEKnologies\KryptX', true);
      Langue := reg.ReadString('Langue');
    Finally reg.Free End;
  End;

  procedure TForm1.FormCreate(Sender: TObject);
  Begin
    installer();

    If ((ParamStr(1) = 'decrypter') And (LowerCase(ExtractFileExt(ParamStr(2))) = '.kpx')) Then
    Begin
      ...
      Application.Terminate();
    End Else
    If (ParamStr(1) = 'crypter') Then
    Begin
      ...
      Application.Terminate();
    End;
  End;

  Procedure desinstaller;
  Var
      res: Integer;
    reg: TRegistry;
    infos: TRegKeyInfo;
  Begin
    res := MessageDlg('Voulez-vous effacer les entrées du registre ?', mtConfirmation, mbYesNoCancel, 0);
    If (res = mrCancel) Then Exit;
    If (res = mrYes) Then
    Begin
      reg := TRegistry.Create;
      Try
        reg.RootKey := HKEY_LOCAL_MACHINE;
        reg.DeleteKey('\SOFTWARE\Microsoft\Windows\CurrentVersion\AppPaths\CR-KryptX.exe');
        reg.DeleteKey('\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\CR-KryptX');
        reg.DeleteKey('\SOFTWARE\CR-TEKnologies\KryptX');
        reg.OpenKey('\SOFTWARE\CR-TEKnologies', true);
        reg.GetKeyInfo(infos);
        If (infos.NumSubKeys = 0) Then reg.DeleteKey('\SOFTWARE\CR-TEKnologies');
      Finally reg.Free End;
    End;

    res := MessageDlg('Vous devez supprimer les fichiers manuellement. Voulez-vous accéder au dossier ?', mtConfirmation, mbYesNoCancel, 0);
    If (res = mrCancel) Then Exit;
    If (res = mrYes) Then ShellExecute(0, nil, PChar(CheminApp), nil, nil, SW_NORMAL);
    Application.Terminate;
  End;

  // Dans le fichier projet (.dpr)
  Begin
    Application.Initialize;
    If ParamStr(1) = 'uninstall' Then desinstaller();
    ...
  End.

Conclusion

Cette méthode d'installation permet donc assez simplement d'installer votre application, en ayant des possibilités très étendues quant à la modification de la base de registre. En revanche elle pêche par la gestion des fichiers, puisque vous ne pourrez pas supprimer l'exécutable, ni créer automatiquement des raccourcis vers votre application.
Mais elle garde un avantage indéniable qui est la taille minimale, puisqu'elle n'augmente pas significativement la taille de votre exécutable compressé.

Si vous avez un problème ou une question, n'hésitez pas à me contacter : contact@crteknologies.fr .



Retour : Accueil > Programmation > Conseils > [haut]
 Logiciels    Wiki     Programmation     Electronique     Projets     Auteur 


Vie privée