71 votes

Comment modifier la variable d'environnement PATH lors de l'exécution d'un programme d'installation d'Inno Setup ?

Inno Setup vous permet de définir des variables d'environnement via les sections [Registre] (en définissant la clé de registre qui correspond à la variable d'environnement).

Cependant, il arrive que l'on ne veuille pas simplement définir une variable d'environnement. Souvent, on veut la modifier. Par exemple : lors de l'installation, on peut vouloir ajouter/supprimer un répertoire à/de la variable d'environnement PATH.

Comment puis-je modifier la variable d'environnement PATH à partir d'InnoSetup ?

91voto

mghie Points 25960

Le chemin dans la clé de registre que vous avez indiqué est une valeur de type REG_EXPAND_SZ . Comme la documentation Inno Setup pour le [Registre] indique qu'il existe un moyen d'y ajouter des éléments :

Sur un string , expandsz ou multisz vous pouvez utiliser une constante spéciale appelée {olddata} dans ce paramètre. {olddata} est remplacé par les données précédentes de la valeur du registre. Le site {olddata} peut être utile si vous devez ajouter une chaîne à une valeur existante, par exemple, {olddata};{app} . Si la valeur n'existe pas ou si la valeur existante n'est pas un type de chaîne, la fonction {olddata} est supprimée en silence.

Pour ajouter au chemin, on peut donc utiliser une section du registre similaire à celle-ci :

[Registry]
Root: HKLM; Subkey: "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"; \
    ValueType: expandsz; ValueName: "Path"; ValueData: "{olddata};C:\foo"

qui ajouterait le " C:\foo "dans le chemin.

Malheureusement, cela se répète lors d'une deuxième installation, ce qui devrait être corrigé également. A Check avec une fonction codée en Pascal script peut être utilisé pour vérifier si le chemin doit effectivement être développé :

[Registry]
Root: HKLM; Subkey: "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"; \
    ValueType: expandsz; ValueName: "Path"; ValueData: "{olddata};C:\foo"; \
    Check: NeedsAddPath('C:\foo')

Cette fonction lit la valeur du chemin d'accès original et vérifie si le répertoire donné y est déjà contenu. Pour ce faire, elle ajoute et ajoute les points-virgules qui sont utilisés pour séparer les répertoires dans le chemin. Pour tenir compte du fait que le répertoire recherché peut être le premier ou le dernier élément, les caractères point-virgule sont également ajoutés à la valeur originale :

[Code]

function NeedsAddPath(Param: string): boolean;
var
  OrigPath: string;
begin
  if not RegQueryStringValue(HKEY_LOCAL_MACHINE,
    'SYSTEM\CurrentControlSet\Control\Session Manager\Environment',
    'Path', OrigPath)
  then begin
    Result := True;
    exit;
  end;
  { look for the path with leading and trailing semicolon }
  { Pos() returns 0 if not found }
  Result := Pos(';' + Param + ';', ';' + OrigPath + ';') = 0;
end;

Notez que vous devrez peut-être développer les constantes avant de les passer en paramètre à la fonction de vérification, voir la documentation pour plus de détails.

La suppression de ce répertoire du chemin d'accès pendant la désinstallation peut être effectuée de manière similaire et est laissée comme un exercice pour le lecteur.

1 votes

Ce ne serait pas génial si vous pouviez simplement passer {olddata} à la fonction Check pour ne pas avoir à relire la valeur dans le code ? (peut-être que vous pouvez - je n'ai pas essayé) ;)

3 votes

Il se peut également que le chemin d'accès soit présent mais qu'il utilise une casse de caractères différente (facilement corrigé en utilisant la commande UpperCase ou une fonction de ce type) ou, pire encore, utiliser les noms de chemins 8.3 (par exemple " C:\Progra ~1 \MyProg ") ou des variables d'environnement (par exemple, "%programfiles%"). \MyProg "). Ce serait un cauchemar de les détecter aussi...

1 votes

Qu'en est-il lorsque votre programme est désinstallé ? Comment supprimer votre chemin d'accès de la variable d'environnement PATH ?

29voto

Wojciech Mleczek Points 1101

J'ai eu le même problème mais malgré les réponses ci-dessus, j'ai fini par trouver une solution personnalisée et j'aimerais la partager avec vous.

Tout d'abord, j'ai créé le environment.iss avec 2 méthodes - l'une pour ajouter le chemin d'accès au fichier d'environnement Chemin d'accès et une seconde pour la supprimer :

[Code]
const EnvironmentKey = 'SYSTEM\CurrentControlSet\Control\Session Manager\Environment';

procedure EnvAddPath(Path: string);
var
    Paths: string;
begin
    { Retrieve current path (use empty string if entry not exists) }
    if not RegQueryStringValue(HKEY_LOCAL_MACHINE, EnvironmentKey, 'Path', Paths)
    then Paths := '';

    { Skip if string already found in path }
    if Pos(';' + Uppercase(Path) + ';', ';' + Uppercase(Paths) + ';') > 0 then exit;

    { App string to the end of the path variable }
    Paths := Paths + ';'+ Path +';'

    { Overwrite (or create if missing) path environment variable }
    if RegWriteStringValue(HKEY_LOCAL_MACHINE, EnvironmentKey, 'Path', Paths)
    then Log(Format('The [%s] added to PATH: [%s]', [Path, Paths]))
    else Log(Format('Error while adding the [%s] to PATH: [%s]', [Path, Paths]));
end;

procedure EnvRemovePath(Path: string);
var
    Paths: string;
    P: Integer;
begin
    { Skip if registry entry not exists }
    if not RegQueryStringValue(HKEY_LOCAL_MACHINE, EnvironmentKey, 'Path', Paths) then
        exit;

    { Skip if string not found in path }
    P := Pos(';' + Uppercase(Path) + ';', ';' + Uppercase(Paths) + ';');
    if P = 0 then exit;

    { Update path variable }
    Delete(Paths, P - 1, Length(Path) + 1);

    { Overwrite path environment variable }
    if RegWriteStringValue(HKEY_LOCAL_MACHINE, EnvironmentKey, 'Path', Paths)
    then Log(Format('The [%s] removed from PATH: [%s]', [Path, Paths]))
    else Log(Format('Error while removing the [%s] from PATH: [%s]', [Path, Paths]));
end;

Référence : RegQueryStringValue , RegWriteStringValue

Maintenant, dans le fichier principal .iss, je pourrais inclure ce fichier et écouter les deux événements (pour en savoir plus sur les événements, consultez le site Web de la Commission européenne). Fonctions des événements dans la documentation), CurStepChanged pour ajouter le chemin après l'installation et CurUninstallStepChanged pour le supprimer lorsque l'utilisateur désinstalle une application. Dans l'exemple ci-dessous script ajoutez/supprimez le fichier bin (relatif au répertoire d'installation) :

#include "environment.iss"

[Setup]
ChangesEnvironment=true

; More options in setup section as well as other sections like Files, Components, Tasks...

[Code]
procedure CurStepChanged(CurStep: TSetupStep);
begin
    if CurStep = ssPostInstall 
     then EnvAddPath(ExpandConstant('{app}') +'\bin');
end;

procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
begin
    if CurUninstallStep = usPostUninstall
    then EnvRemovePath(ExpandConstant('{app}') +'\bin');
end;

Référence : ExpandConstant

Note 1 : Installer l'étape ajouter le chemin une seule fois (assure la répétitivité de l'installation).

Note #2 : L'étape de désinstallation ne supprime qu'une seule occurrence du chemin de la variable.

Bonus : Étape d'installation avec case à cocher "Ajouter à la variable PATH" .

Inno Setup - Add to PATH variable

Pour ajouter une étape d'installation avec une case à cocher "Ajouter à la variable PATH" définir une nouvelle tâche dans [Tasks] (cochée par défaut) :

[Tasks]
Name: envPath; Description: "Add to PATH variable" 

Ensuite, vous pouvez le vérifier CurStepChanged événement :

procedure CurStepChanged(CurStep: TSetupStep);
begin
    if (CurStep = ssPostInstall) and IsTaskSelected('envPath')
    then EnvAddPath(ExpandConstant('{app}') +'\bin');
end;

18voto

eee Points 1832

Vous pouvez utiliser les services de LegRoom.net. modpath.iss script dans votre fichier InnoSetup script :

#define MyTitleName "MyApp" 

[Setup]
ChangesEnvironment=yes

[CustomMessages]
AppAddPath=Add application directory to your environmental path (required)

[Files]
Source: "install\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs; 

[Icons]
Name: "{group}\{cm:UninstallProgram,{#MyTitleName}}"; Filename: "{uninstallexe}"; Comment: "Uninstalls {#MyTitleName}"
Name: "{group}\{#MyTitleName}"; Filename: "{app}\{#MyTitleName}.EXE"; WorkingDir: "{app}"; AppUserModelID: "{#MyTitleName}"; Comment: "Runs {#MyTitleName}"
Name: "{commondesktop}\{#MyTitleName}"; Filename: "{app}\{#MyTitleName}.EXE"; WorkingDir: "{app}"; AppUserModelID: "{#MyTitleName}"; Comment: "Runs {#MyTitleName}"

[Registry]
Root: HKLM; Subkey: "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"; ValueType: expandsz; ValueName: "Path"; ValueData: "{olddata};{app}"

[Tasks]
Name: modifypath; Description:{cm:AppAddPath};   

[Code]

const
    ModPathName = 'modifypath';
    ModPathType = 'system';

function ModPathDir(): TArrayOfString;
begin
    setArrayLength(Result, 1)
    Result[0] := ExpandConstant('{app}');
end;

#include "modpath.iss"

0 votes

Merci, ça marche comme sur des roulettes. En supprimant quelques morceaux de code dans modpath.iss, il est également possible de le faire fonctionner sans demander à l'utilisateur (c'est-à-dire pas comme une tâche avec case à cocher mais toujours).

0 votes

@JohannesSchaub-litb Wow, c'est lourd. Depuis le lien original est maintenant marqué comme dangereux (je ne sais pas pourquoi), mais une simple recherche dans l'archive a noté le script comme une licence GPL3 web.archive.org/web/20170610232441/https://www.legroom.net/

8voto

Helen Dyakonova Points 56

El NeedsAddPath en la réponse de @mghie ne vérifie pas la fin de la phrase \ et la casse des lettres. Réparez-le.

function NeedsAddPath(Param: string): boolean;
var
  OrigPath: string;
begin
  if not RegQueryStringValue(
    HKEY_LOCAL_MACHINE,
    'SYSTEM\CurrentControlSet\Control\Session Manager\Environment',
    'Path', OrigPath)
  then begin
    Result := True;
    exit;
  end;
  { look for the path with leading and trailing semicolon }
  { Pos() returns 0 if not found }
  Result :=
    (Pos(';' + UpperCase(Param) + ';', ';' + UpperCase(OrigPath) + ';') = 0) and
    (Pos(';' + UpperCase(Param) + '\;', ';' + UpperCase(OrigPath) + ';') = 0); 
end;

3 votes

Comment utiliser une variable au lieu de ' C:\foo ' ? J'ai essayé NeedsAddPath('{app}') mais cela ne fonctionne pas - je concatène juste le chemin alors qu'il est déjà sorti. Pouvez-vous me conseiller ?

1 votes

Juste pour répondre au commentaire précédent, cela pourrait être utile à d'autres : Vous devez utiliser ExpandConstant() fonction.

0 votes

Merci, Jack. Cependant, j'aimerais voir un exemple de NeedsAddPath('{app} \MoreDirectoriesHere ')

3voto

decaffeinated Points 46

Je tiens à remercier tout le monde pour leurs contributions à cette question. J'ai incorporé environ 95 % du code posté par Wojciech Mleczek dans l'installateur de mon application. J'ai cependant quelques corrections à apporter à ce code qui pourraient s'avérer utiles à d'autres. Mes modifications :

  • Renommé argument formel Path a instlPath . Réduit les utilisations multiples de "Path" dans le code (plus facile à lire, IMO).

  • Lors de l'installation/désinstallation, ajoutez une vérification de l'existence d'un fichier de type instlPath qui se termine par \; .

  • Pendant l'installation, ne doublez pas ; dans l'actuel %PATH% .

  • Poignée manquante ou vide %PATH% pendant l'installation.

  • Pendant la désinstallation, assurez-vous qu'un indice de départ de 0 n'est pas transmis à Delete() .

Voici ma version mise à jour de EnvAddPath() :

const EnvironmentKey = 'Environment';

procedure EnvAddPath(instlPath: string);
var
    Paths: string;
begin
    { Retrieve current path (use empty string if entry not exists) }
    if not RegQueryStringValue(HKEY_CURRENT_USER, EnvironmentKey, 'Path', Paths) then
        Paths := '';

    if Paths = '' then
        Paths := instlPath + ';'
    else
    begin
        { Skip if string already found in path }
        if Pos(';' + Uppercase(instlPath) + ';',  ';' + Uppercase(Paths) + ';') > 0 then exit;
        if Pos(';' + Uppercase(instlPath) + '\;', ';' + Uppercase(Paths) + ';') > 0 then exit;

        { Append App Install Path to the end of the path variable }
        Log(Format('Right(Paths, 1): [%s]', [Paths[length(Paths)]]));
        if Paths[length(Paths)] = ';' then
            Paths := Paths + instlPath + ';'  { don't double up ';' in env(PATH) }
        else
            Paths := Paths + ';' + instlPath + ';' ;
    end;

    { Overwrite (or create if missing) path environment variable }
    if RegWriteStringValue(HKEY_CURRENT_USER, EnvironmentKey, 'Path', Paths)
    then Log(Format('The [%s] added to PATH: [%s]', [instlPath, Paths]))
    else Log(Format('Error while adding the [%s] to PATH: [%s]', [instlPath, Paths]));
end;

Et une version mise à jour de EnvRemovePath() :

procedure EnvRemovePath(instlPath: string);
var
    Paths: string;
    P, Offset, DelimLen: Integer;
begin
    { Skip if registry entry not exists }
    if not RegQueryStringValue(HKEY_CURRENT_USER, EnvironmentKey, 'Path', Paths) then
        exit;

    { Skip if string not found in path }
    DelimLen := 1;     { Length(';') }
    P := Pos(';' + Uppercase(instlPath) + ';', ';' + Uppercase(Paths) + ';');
    if P = 0 then
    begin
        { perhaps instlPath lives in Paths, but terminated by '\;' }
        DelimLen := 2; { Length('\;') }
        P := Pos(';' + Uppercase(instlPath) + '\;', ';' + Uppercase(Paths) + ';');
        if P = 0 then exit;
    end;

    { Decide where to start string subset in Delete() operation. }
    if P = 1 then
        Offset := 0
    else
        Offset := 1;
    { Update path variable }
    Delete(Paths, P - Offset, Length(instlPath) + DelimLen);

    { Overwrite path environment variable }
    if RegWriteStringValue(HKEY_CURRENT_USER, EnvironmentKey, 'Path', Paths)
    then Log(Format('The [%s] removed from PATH: [%s]', [instlPath, Paths]))
    else Log(Format('Error while removing the [%s] from PATH: [%s]', [instlPath, Paths]));
end;

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X